LeetCode704.二分查找、27.移除元素、977.有序数组的平方

Leetcode 704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1

你必须编写一个具有 O(log n) 时间复杂度的算法。

题目链接:

https://leetcode.cn/problems/binary-search/

文章讲解:

https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html

视频讲解:

https://www.bilibili.com/video/BV1fA4y1o715

我的思路:

在数组的第一个位置和最后一个位置分别设置一个low指针和一个high指针,在数组的中间位置(即mid=(low+high)/2)设置一个mid指针。

若mid指针所指元素小于目标值target,则将low指针移动到mid指针所指的右侧第一个位置。

若mid指针所指元素大于目标值target,则将high指针移动到mid指针所指的左侧第一个位置。

若mid指针所指元素等于目标值target,则返回mid所指位置的下标。

若mid不等于target,则重复上述判断。

若数组的每一个元素都不等于target,则返回-1。

我的代码:

int search(int* nums, int numsSize, int target) { int low=0,high=numsSize-1; while(low<=high){ int mid=(low+high)/2; if(nums[mid]==target) return mid; else if(nums[mid]<target) low=mid+1; else if(nums[mid]>target) high=mid-1; } return -1; }

二分查找法,也称折半查找法,只适用于有序数组,且数组中无重复元素

(若有重复元素,则返回的元素下标可能不是唯一的。)

写二分法要注意区间边界,区间的定义一般分为两种:左闭右闭即[low, high]和左闭右开即[low, high)。

第一种写法:左闭右闭

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

代码如下:(就是我前面的想法)

int search(int* nums, int numsSize, int target) { int low=0,high=numsSize-1; while(low<=high){ int mid=(low+high)/2; if(nums[mid]<target) low=mid+1; else if(nums[mid]>target) high=mid-1; else return mid; } return -1; }

第二种写法:左闭右开

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

代码如下:(不习惯用这个)

int search(int* nums, int numsSize, int target) { int low=0,high=numsSize; while(low<high){ int mid=(low+high)/2; if(nums[mid]<target) low=mid+1; else if(nums[mid]>target) high=mid; else return mid; } return -1; } 
相关题目推荐35.搜索插入位置(opens new window)34.在排序数组中查找元素的第一个和最后一个位置(opens new window)69.x 的平方根(opens new window)367.有效的完全平方数

LeetCode 27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。返回 k

题目链接:

https://leetcode.cn/problems/remove-element/

文章讲解:

​​​​​​https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html

视频讲解:

https://www.bilibili.com/video/BV12A4y1Z7LP

我的思路:

设置两个指针 i 和 j ,初始均指向数组的第一个元素,i 用于记录最新非删除元素的位置,j 用于向后查找需删除的元素,再设置一个int型变量 k 记录循环的轮数。

当 k 小于数组长度时,进入循环:

若 j 所指的元素不等于val,则将 j 所指元素赋值到 i 所指元素上,i 和 j 一起向后移动一个元素。

若 j 所指的元素等于val,则只将 j 向后移动一个元素,继续寻找需删除的元素。

以上判断结束后,轮数 k 加1,知道不满足条件为止,退出循环,返回 i。

我的代码:

int removeElement(int* nums, int numsSize, int val) { int i=0,j=0,k=0; while(k<numsSize){ if(nums[j]!=val && i<=j){ nums[i]=nums[j]; i++; j++; } else if(nums[j]==val) j++; k++; } return i; }

暴力解

两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。时间复杂度是O(n^2)。

代码如下:

int removeElement(int* nums, int numsSize, int val) { for(int i=0;i<numsSize;i++){ if(nums[i]==val){// 发现需要移除的元素,就将数组集体向前移动一位 for(int j=i+1;j<numsSize;j++) nums[j-1]=nums[j]; i--;// 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位 numsSize--;// 此时数组的大小-1 } } return numsSize; }

快慢指针

通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

代码如下:(和我写的差不多)

int removeElement(int* nums, int numsSize, int val) { int slow=0; for(int fast=0;fast<numsSize;fast++) if(nums[fast]!=val) nums[slow++]=nums[fast]; return slow; }
相关题目推荐26.删除排序数组中的重复项(opens new window)283.移动零(opens new window)844.比较含退格的字符串(opens new window)977.有序数组的平方

LeetCode 977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

题目链接:

https://leetcode.cn/problems/squares-of-a-sorted-array/

文章讲解:

https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html

视频讲解:

https://www.bilibili.com/video/BV1QB4y1D7ep

双指针法

数组其实是有序的, 只不过负数平方后可能成为最大数,那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

i 指向数组起始位置,j 指向数组终止位置。

定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。

如果A[i] * A[i] < A[j] * A[j] ,那么result[k--] = A[j] * A[j]; 。

如果A[i] * A[i] >= A[j] * A[j] ,那么result[k--] = A[i] * A[i]; 。

int* sortedSquares(int* nums, int numsSize, int* returnSize) { //数据类型* 数组名 = (数据类型*)malloc(数组长度 * sizeof(数据类型)); int* result=(int*)malloc(numsSize*sizeof(int)); int k=numsSize-1; for(int i=0,j=numsSize-1;i<=j;){ if(nums[i]*nums[i]>nums[j]*nums[j]){ result[k--]=nums[i]*nums[i]; i++; } else{ result[k--]=nums[j]*nums[j]; j--; } } *returnSize = numsSize;//设置返回数组的大小 return result; }

注意:return只能直接返回一个值 / 一个指针,无法直接返回多个独立值。

        如果删掉*returnSize = numsSize;,会导致:

  1. 调用者无法获取返回数组的长度:调用者拿到result指针后,不知道该遍历多少次(比如调用者写for(int i=0; i<returnSize; i++)returnSize是随机值,会导致数组越界、程序崩溃);
  2. 违反题目隐含要求:OJ(在线判题系统)会通过returnSize校验返回数组的长度是否正确,直接判错。

暴力解

每个数平方之后,进行快速排序。

Read more

Java synchronized关键字详解:从入门到原理(两课时)

Java synchronized关键字详解:从入门到原理(两课时)

文章目录 * 适用对象 * 学习目标 * 课程安排 * 第一课时:synchronized基础与使用 * 1.1 从一个线程安全问题开始 * 1.2 synchronized是什么? * 1.3 初识synchronized的三种用法 * 1.3.1 修饰实例方法 * 1.3.2 修饰静态方法 * 1.3.3 修饰代码块 * 1.4 深入理解锁的范围 * 1.4.1 三种锁的对比表格 * 1.4.2 常见面试题解析 * 1.5 synchronized的核心特性 * 1.5.1 可重入性 * 1.5.2 可见性保证 * 1.

By Ne0inhk
Java static避坑:静态与非静态访问规则全解析

Java static避坑:静态与非静态访问规则全解析

🏠个人主页:黎雁 🎬作者简介:C/C++/JAVA后端开发学习者 ❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生 ✨ 从来绝巘须孤往,万里同尘即玉京 文章目录 * Java static避坑:静态与非静态访问规则全解析 * 📝 文章摘要 * 一、先搞懂:访问规则的底层逻辑 🧠 * 二、三大核心访问规则(必记)✅ * 规则1:静态方法 → 静态成员 ✅ 允许 * 正确案例:静态方法调用静态变量/方法 * 规则2:静态方法 → 非静态成员 ❌ 禁止(直接访问) * 错误案例:静态方法直接访问非静态成员 * 特殊情况:静态方法间接访问非静态成员(不推荐) * 规则3:非静态方法 → 静态/非静态成员 ✅ 全允许

By Ne0inhk
JAVA IO流进阶:字符流与字节流的深度应用

JAVA IO流进阶:字符流与字节流的深度应用

JAVA IO流进阶:字符流与字节流的深度应用 1.1 本章学习目标与重点 💡 掌握字节流与字符流的核心区别,能够根据实际开发场景选择合适的IO流实现文件操作。 💡 熟练运用缓冲流提升IO操作效率,解决大文件读写的性能问题。 💡 理解转换流的作用,处理不同编码格式的文件读写,避免乱码问题。 ⚠️ 本章重点是流的嵌套使用和资源释放的标准写法,这是实际开发中高频考点和易错点。 1.2 字节流与字符流的核心差异(七千字以上内容展开) 1.2.1 基本概念与设计初衷 💡 字节流以byte为基本单位进行数据传输,它可以处理所有类型的文件,比如图片、视频、音频、文本等。 字符流以char为基本单位进行数据传输,它专门用于处理文本文件,底层会涉及字符编码的转换。 字节流的核心类是InputStream和OutputStream,字符流的核心类是Reader和Writer。 两者都是抽象类,实际开发中我们使用的是它们的子类,比如FileInputStream、FileWriter等。 ✅ 核心结论:处理非文本文件用字节流,处理文本文件优先用字符流。 1.2.2 代码实操:字

By Ne0inhk
Java 部署:Jenkins Pipeline 构建 Java 项目(自动化)

Java 部署:Jenkins Pipeline 构建 Java 项目(自动化)

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java部署这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Java 部署:Jenkins Pipeline 构建 Java 项目(自动化) 🚀 * 为什么选择 Jenkins Pipeline?🔧 * 环境准备:搭建 Jenkins 服务器 ⚙️ * 使用 Docker 快速启动 Jenkins * 安装必要插件 * 示例 Java 项目:一个简单的 Spring Boot 应用 🌱 * 项目结构 * `pom.xml` * `DemoApplication.java` * `HelloController.java` * 单元测试(可选但推荐) * 编写 Jenkins

By Ne0inhk