二分查找
不必纠结每次中间的数字两边的数字数量不一样,
真正影响的是中间那个数字到底该不该加入下一次的查找中
左闭右闭 [left, right]
int search(int* nums, int numsSize, int target) {
int left = 0;
int right = numsSize - 1;
while (left <= right) {
int middle = left + (right - left) / 2;
if (target < nums[middle]) {
right = middle - 1;
} else if (target > nums[middle]) {
left = middle + 1;
} else {
return middle;
}
}
return -1;
}
左闭右开 [left, right)
int search(int* nums, int numsSize, int target) {
int left = 0;
int right = numsSize;
while (left < right) {
int middle = left + (right - left) / 2;
if (target < nums[middle]) {
right = middle;
} else if (target > nums[middle]) {
left = middle + 1;
} else {
return middle;
}
}
return -1;
}
模版
int searchInsert(int nums[], int numsSize, int target) {
int left = 0;
int right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// 处理相等的情况,例如返回 mid 或执行相关逻辑
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// 处理其他情况,这里可以返回 left 或 right + 1,具体取决于需求
return left;
}
要点
返回值
实际上要看判断条件和while()条件来判断返回值的内容,一般以 left 为主,如果 ==mid的条件放在上面,基本就是两个返回值,一个是当找到了mid 就返回了 另一个就是没有找到 返回false等
int searchInsert(int* nums, int numsSize, int target) {
int left = 0;
int right = numsSize - 1;
int mid = 0;
while(left <= right){
mid = left + (right - left)/2;
if(nums[mid] < target) {
left = mid + 1;
} else if(nums[mid] > target) {
right = mid - 1;
} else if(nums[mid] == target) {
return mid;
}
}
return left;
}
思考为何目标值不存在于数组中,返回left
nums = [1,3,5,6], target = 0
nums = [1,3,5,6], target = 2
当目标值不存在于数组中,最后一次循环进入条件left==right
两种情况:nums[mid] < target
目标值在当前后一个,应当插入位置为当前mid+1
此时进行操作left = mid + 1
,并且此时right = mid
nums[mid] > target
目标值在当前前一个,应当插入位置为当前mid
此时进行操作right = mid - 1
,并且此时left = mid
由此可以得到我们可以返回left
或者right+1
在力扣提交都是可以通过的。
同理看看这道题,最后应当return什么?
循环结束的最后一次left = right
输入:n = 5, bad = 4
对对对错错
此时经过不断地缩小范围,进入循环后mid指向第4个版本(所求答案)
此时left = right = mid
一定是错误正确版本的交接的两个的一个
若此时为错误版本进行right = mid - 1
操作,此时 left = mid
,而所求答案是mid
若此时为正确版本进行left = mid + 1
操作,此时right = mid
,而所求答案是mid + 1
由此可以得到我们可以返回left
或者right+1
思考此时 mid 一定是错误正确版本的交接的两个的一个 还是 唯一后面的错误版本或者前面的正确版本
- 循环条件:
- 循环条件是
left <= right
,即当left
和right
相等时仍然继续循环。 - 这确保了循环结束时
left
和right
指向的是同一个位置。
- 循环条件是
- 更新操作:
- 当
isBadVersion(mid)
为真时(错误版本),更新right = mid - 1
,使得right
指向错误版本之前的位置。 - 当
isBadVersion(mid)
为假时(正确版本),更新left = mid + 1
,使得left
指向正确版本之后的位置。
- 当
int firstBadVersion(int n) {
int left = 0;
int right = n;
int mid = 0;
while(left <= right) {
mid = left + (right - left)/2;
if(isBadVersion(mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
红蓝染色法
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
核心要素
注意区间开闭,三种都可以
循环结束条件:当前区间内没有元素
下一次二分查找区间:不能再查找(区间不包含)mid
,防止死循环
返回值:大于等于target
的第一个下标(注意循环不变量)
循环不变式描述为L的左边恒小于x,R的右边恒大于等于x
四种类型
有序数组中二分查找的四种类型(下面的转换仅适用于数组中都是整数)
- 第一个大于等于x的下标:
low_bound(x)
- 第一个大于x的下标:可以转换为
第一个大于等于 x+1 的下标
,low_bound(x+1)
- 最后一个一个小于x的下标:可以转换为
第一个大于等于 x 的下标
的左边位置
,low_bound(x) - 1
; - 最后一个小于等于x的下标:可以转换为
第一个大于等于 x+1 的下标
的左边位置
,low_bound(x+1) - 1;
如有错误烦请指正。
感谢您的阅读