二分查找
本文最后更新于:8 个月前
思路简单,细节复杂!
二分查找
一、3种题型
1、查找目标值是否存在
使用二分查找指定目标值,只要找到目标值就立即返回其下标。
当右指针为最后一个元素的下标
r = len(nums) - 1
时,查找区间为闭区间[l, r]
,只要在这个区间内的元素都应该被查找。当
l > r
时没有元素可以再被查找,所以循环条件为l <= r
。为了防止数组长度过大,
l + r
溢出,使用mid = l + (r - l) // 2
计算二分位置。target == nums[mid]
,因为只要找到一个相同的元素即可,所以相等时立即返回mid
。当
target
不等于nums[mid]
时,因为查找范围是闭区间[l, r]
,所以缩小范围的时候,依然保持闭区间,target < nums[mid]
,移动右指针缩小查找范围,此时查找范围是[l, mid-1]
target > nums[mid]
,移动左指针缩小查找范围,此时查找范围是[mid+1, r]
最后一次查找是当
l = r
时,如果这次还没有查找到,即查找失败,return -1
1 |
|
- 左闭右闭,
[left, right]
- 循环条件,
left <= right
- 指针移动,
left = mid + 1; right = mid -1
2、查找目标值左边界
查找目标值的左边界,找到 target
时不要立即返回,而是缩小搜索区间的右指针 r
,在左闭右开区间 [l, mid)
中继续搜索,即不断向左收缩,达到锁定左边界的目的。
当右指针为数组长度
r = len(nums)
,即最后一个元素下标加一时,查找范围是[l, r)
。当
l = r
时没有元素可以再被查找,所以循环条件为l < r
。为了防止数组长度过大,
l + r
溢出,使用mid = l + (r - l) // 2
计算二分位置。target == nums[mid]
,因为要找到目标元素的左边界,所以相等时不返回mid
,而是以mid
为右指针缩小查找区间,因为查找范围是左闭右开区间[l, r)
,所以缩小范围的时候,依然保持左开右闭区间,为[l, mid)
。当
target
不等于nums[mid]
时target < nums[mid]
,缩小查找范围右边界,此时查找范围是[l, mid)
target > nums[mid]
,缩小查找范围左边界,此时查找范围是[mid+1, r)
最后一次查找是当
l = r-1
时,这次循环完成后l = r
,此时标志循环结束- 如果
l = len(nums)
,说明目标值大于数组中所有元素,查找失败,return -1
; - 如果
nums[l] != target
,说明数组中没有这个值,查找失败,return -1
- 如果非上述两种情况,这时
l = r
,返回l
或者r
即为target
左边界。
- 如果
1 |
|
- 左闭右开,
[left, right)
- 循环条件,
left < right
- 下标移动,
left = mid + 1; right = mid
- mid与target相等时,区间继续向左移动,
R = mid
3、查找右边界
找到 target
时不要立即返回,而是缩小搜索区间的左指针 l
,在区间 [mid+1, r)
中继续搜索,即不断向右收缩,达到锁定右边界的目的。
当右指针为数组长度
r = len(nums)
,即最后一个元素下标加一时,查找范围是[l, r)
。当
l = r
时没有元素可以再被查找,所以循环条件为l < r
。为了防止数组长度过大,
l + r
溢出,使用mid = l + (r - l) // 2
计算二分位置。target == nums[mid]
,因为要找到目标元素的右边界,所以相等时不返回mid
,而是以mid+1
为左指针缩小查找区间,因为查找范围是左闭右开区间[l, r)
,所以缩小范围的时候,依然保持左开右闭区间,为[mid+1, r)
。当
target
不等于nums[mid]
时target < nums[mid]
,缩小查找范围右边界,此时查找范围是[l, mid)
target > nums[mid]
,缩小查找范围左边界,此时查找范围是[mid+1, r)
最后一次查找是当
l = r - 1
时,这次循环完成后l = r
,此时标志循环结束- 如果
r = 0
,说明目标值小于数组中所有元素,查找失败,return -1
; - 如果
nums[l-1] != target
,说明数组中没有这个值,查找失败,return -1
- 如果非上述两种情况,因为在
target = nums[mid]
的时候执行的是l = mid + 1
,又因此时l = r
所以返回l - 1
或者r - 1
即为target
右边界。
- 如果
1 |
|
- 左闭右开,
[left, right)
- 循环条件,
left < right
- 下标移动,
left = mid + 1; right = mid
- mid与target相等时,区间继续向右移动,
L = mid + 1