1. Leetcode704 二分查找(二分法)
1. 理论
2. 总结
一、模版(认真看看3种情况之间的关系)
- 左右闭区间:[a, b]
- 循环条件
- ⭐
start ≤ end
- ⭐
- 边界更新操作
tmp = (start + end) / 2
if (tmp < target) start = tmp + 1;
if (tmp > target) end = tmp - 1;
- 循环条件
- 左闭右开:[a, b)
- 循环条件
- ⭐
start < end
(左右相等没意义)
- ⭐
- 边界更新操作
tmp = (start + end) / 2
if (tmp < target) start = tmp + 1;
- ⭐
if (tmp > target) end = tmp;
(开区间不需要越位)
- 循环条件
左开右闭:(a, b](一般不考虑这种情况,前两种已经足够)- 循环条件
- ⭐
start < end
(左右相等没意义)
- ⭐
- 边界更新操作
tmp = (start + end) / 2
- ⭐
if (tmp < target) start = tmp;
(开区间不需要越位) if (tmp > target) end = tmp - 1;
- 循环条件
二、⭐⭐需要考虑的边界值条件
- 数组为空(nums: [])
- 指针越界(start > length - 1;end < 0)
三、其他需要考虑的点
- 数据类型是否溢出(int → long)
四、进阶规律:结束状态时,right指针的位置有规律
例题:35. 搜索插入位置
结论:
- ⭐在左闭右开[a, b)的情况下,结束状态时,若target < arr.length - 1(target不在最后一个),right总会在target的右侧一个元素(不管target是否存在于arr)。
- 原因:右侧是开区间
- 应用:用来找边界范围
- 注意:如果right在最后一个,记得讨论不同情况
- 在左闭右闭[a, b]的情况下,结束状态时,若target < arr.length - 1(target不在最后一个),right总会在符合target的最后一个位置;若不存在target,则在target前一个最近的位置。
- 注意:找相同元素边界的话,arr[mid] == target归到移动左指针的条件中
3. 进阶力扣题目
A. 35. 搜索插入位置
力扣https://leetcode-cn.com/problems/search-insert-position/
// 1. 左闭右闭 [a,b]
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int mid = 0;
// 最终left指针会停到最近大于target的位置
while (left <= right) {
mid = (left + right) / 2;
if (target == nums[mid]) {
return mid;
} else if (target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 2. 左闭右开 [a,b)
public int searchInsert(int[] nums, int target) {
// 左闭右开
int left = 0;
int right = nums.length;
int mid = 0;
// 3个指针会停到大于等于target的位置
while (left < right) {
mid = (left + right) / 2;
if (target == nums[mid]) {
return mid;
} else if (target < nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
// 单独处理:如果target比数组最后的数还大
if (target > nums[nums.length - 1]) return nums.length;
return left;
}
力扣https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
思路:先找左边界,后找右边界
难点:边界值分析
情况1:数组为空
情况2:指针越界
public int[] searchRange(int[] nums, int target) {
int start = 0;
int end = nums.length - 1;
int[] arr = new int[2];
arr[0] = -1;
arr[1] = -1;
//边界值1:如果数组为空,返回[-1,-1]
if (nums.length == 0) return arr;
//找左边界
while (start <= end) {
int tmp = (start + end) / 2;
if (nums[tmp] < target) start = tmp + 1;
else end = tmp - 1;
}
//边界值2:start超过边界(nums = [1],target = 2)
//start与end汇合时值不等于target(target不存在),[-1,-1]
if (start >= nums.length || nums[start] != target) {
return arr;
}
arr[0] = start;
//找右边界
end = nums.length - 1;
while (start <= end) {
int tmp = (start + end) / 2;
if (nums[tmp] <= target) start = tmp + 1;
else end = tmp - 1;
}
arr[1] = end;
return arr;
}
二刷总结:写二分法还是有点生疏,左闭右开与左闭右闭的循环条件与二分执行语句需要想一阵子才能回忆起来。
本章用时:1h
2. Leetcode27 移除元素
1. 题目链接:27. 移除元素
2. 第一想法&题解:自然想到双指针法,比较容易理解。代码如下:
// 双指针法
public int removeElement(int[] nums, int val) {
int left = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] == val) continue;
else {
nums[left] = nums[right];
left++;
}
}
return left;
}
3. 重点难点:左右指针应该遍历右指针,并且根据特定条件更新左指针
4. 用时:30min