二分查找的思路不难理解,但是边界条件容易出错,比如 循环结束条件中 left 和 right 的关系,更新 left 和 right 位置时要不要加 1 减 1。到底是 while(left < right)
还是 while(left <= right)
,到底是right = middle
呢,还是要right = middle - 1
呢?
第一种写法,我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] (这个很重要非常重要)。
区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
二分模板
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length - 1; // 注意
while(left <= right) { // 注意
int mid = (left + right) / 2; // 注意
if(nums[mid] == target) { // 注意
// 相关逻辑
} else if(nums[mid] < target) {
left = mid + 1; // 注意
} else {
right = mid - 1; // 注意
}
}
// 相关返回值
return 0;
}
}
35.搜索插入位置
每次根据 nums[mid] 和 target 之间的大小进行判断,相等则直接返回下标,nums[mid] < target 则 left 右移,nums[mid] > target 则 right 左移
查找结束如果没有相等值则返回 left,该值为插入位置
时间复杂度:O(logn)
为什么直接return left?
因为如果上面的没有返回return middle,说明最后一定是,left>right从而跳出循环的,在此之前是left=right,如果最后是right-1导致的left>right,说明原来的right位置是大于target的,所以返回原来的right位置即left位置;
如果最后是left+1导致的left>right,说明是原来的的left=right这个位置小于target,而right能移动到这个位置,说明此位置右侧是大于target的,left现在加1就移动到了这样的位置,返回left即可
作者:画手大鹏
链接:https://leetcode.cn/problems/search-insert-position/solutions/8017/hua-jie-suan-fa-35-sou-suo-cha-ru-wei-zhi-by-guanp/
来源:力扣(LeetCode
74.搜索二维矩阵
方法一:两次二分,先搜索列再搜索行
方法二:与240题一样的抽象BST做法
34. 在排序数组中查找元素的第一个和最后一个位置
思路
二分查找左右边界
- 不可以找到
target
以后,然后向两边扩散(线性查找),这样的话时间复杂度为 O(N) - 找
target
第一次出现的位置和最后一次出现的位置的时候,都只能用「二分查找」才符合题目的意思,注意分类讨论,并且把分类讨论的结果合并。
33.搜索排序数组
思路
先判断哪半段是有序的,然后判断目标是否在有序的那半里面
153. 寻找旋转排序数组的最小值
在这个数组中,至少有一个地方,数组的顺序被打破,这个打破的地方就是无序区间。
证明
-
定义有序与无序:
- 假设数组
nums
的长度为n
,且经过旋转后形成两部分:有序部分和无序部分。 - 有序部分是指在某个区间内所有元素是升序排列的;无序部分是指该区间内的元素顺序被打破。
- 假设数组
-
无序区间的性质:
- 如果我们在数组中找到一个无序区间,那么这个区间的左端点一定是该区间的最大值,而右端点是最小值。例如,在
[4, 5, 1, 2, 3]
中,4
和5
是有序的,而1
是无序区间的起始点,最小值在无序区间。
- 如果我们在数组中找到一个无序区间,那么这个区间的左端点一定是该区间的最大值,而右端点是最小值。例如,在
-
推理步骤:
- 当你选择某个元素
mid
并比较nums[left]
和nums[mid]
时,你可以得出以下结论:- Case 1: 如果
nums[left] <= nums[mid]
,则说明从left
到mid
这个区间是有序的。- 在这种情况下,最小值一定不在这个有序区间内,而是在右边的无序部分中。
- Case 2: 如果
nums[left] > nums[mid]
,则说明从left
到mid
这个区间是无序的。- 在这种情况下,最小值必然位于这个无序部分中,因为它是被旋转打乱的区间,包含了原始数组的最小元素。
- Case 1: 如果
- 当你选择某个元素
-
结束条件:
- 当
left
和right
重合时(即left == right
),此时nums[left]
(或nums[right]
)就是数组中的最小值。
- 当
直观理解
- 设想你在一个有序的数组中找最小值,任何时候你都能找到最小值,因为它不会出现在有序区间的末尾。
- 当数组被旋转,分成有序部分和无序部分时,最小值必定是在这个无序部分,因为有序部分的最小值比无序部分的任何值都要大。
4. 寻找两个正序数组的中位数
题解:力扣(LeetCode)配合 官方题解的视频使用