最常见的二分查找
704. 二分查找
示例 1:
输入:
nums
= [-1,0,3,5,9,12],target
= 9
输出: 4
解释: 9 出现在nums
中并且下标为 4
示例 2:
输入:
nums
= [-1,0,3,5,9,12],target
= 2
输出: -1
解释: 2 不存在nums
中因此返回 -1
代码
1 | def search(self, nums, target): |
寻找左边界
278. 第一个错误的版本
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
示例 2:
输入:n = 1, bad = 1
输出:1
如果使用range(n)
遍历会产生溢出问题
代码
1 | def firstBadVersion(self, n): |
寻找左右边界
LCR 172. 统计目标成绩的出现次数
示例 1:
输入: scores = [2, 2, 3, 4, 4, 4, 5, 6, 6, 8], target = 4
输出: 3
示例 2:
输入: scores = [1, 2, 3, 5, 7, 9], target = 6
输出: 0
原理
- 初始化: 左边界 left=0 ,右边界 right=len(scores)−1。
- 循环二分: 当闭区间
[left,right]
无元素时跳出;- 计算中心点:mid=left+(right-left)//2
- if scores[mid]<target:说明target在闭区间[mid+1,right]中,则left=mid+1
- if scores[mid]>target:说明target在闭区间[left,mid-1]中,则right=mid-1
- if scores[mid]==target:则需要分情况讨论
- 如果是找左边界,则左边界一定在较小的那一边,因此左边界一定在[left,mid-1]区间,则right=mid-1
- 如果是找右边界,则left=mid+1
代码
- 未优化版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30def countTarget(self, scores, target):
"""
:type scores: List[int]
:type target: int
:rtype: int
"""
# 寻找左边界
left,right=0,len(scores)-1
while left<=right:
mid = left+(right-left)//2
if scores[mid]<target: # 说明target在[mid+1,right]中
left=mid+1
elif scores[mid]>target:
right=mid-1
elif scores[mid]==target: #如果相等,说明左边界在[left,mid-1]
right=mid-1
final_left=left
# 寻找右边界
left,right=0,len(scores)-1
while left<=right:
mid = left+(right-left)//2
if scores[mid]<target: # 说明target在[mid+1,right]中
left=mid+1
elif scores[mid]>target:
right=mid-1
elif scores[mid]==target: #如果相等,说明左边界在[left,mid-1]
left=mid+1
final_right=left
return final_right-final_left