题目描述
在升序数组里找 target,返回它的下标;找不到返回 -1。
思路解析
用 l、r 圈出搜索范围,mid 取正中间。中间的数比目标小,答案一定在右半边;比目标大,答案在左半边。每次范围都减半。
准备:左指针 l=0,右指针 r=5,范围是整个数组。
第 1 次 · 取中间:中间下标 mid = (0+5)//2 = 2,nums[2] = 5。拿它和目标 9 比。
第 1 次 · 判断:5 比 9 小,说明目标在右半边。把左指针移到 mid+1 = 3,左边那半(含 5)整个丢掉。
第 2 次 · 取中间:现在范围是 [3, 5]。新的中间 mid = (3+5)//2 = 4,nums[4] = 9。
第 2 次 · 命中!:nums[4] 正好等于 9!返回下标 4。只用了 2 次比较。
每次砍一半,是 O(log n)。这就是为什么字典查字、猜数字游戏都用二分。
① 循环条件是 l ≤ r(注意等号);② mid 用 l + (r−l)//2 防止溢出;③ 比较后 l 要 mid+1、r 要 mid−1,不能原地不动,否则会死循环。
再看个找不到的情况:换个例子:找 6。数组里没有 6,看二分怎么收尾。
mid=2 · 5<6:nums[2]=5 < 6,l 移到 3。
mid=4 · 9>6:nums[4]=9 > 6,r 移到 3。现在 l 和 r 都指向 3。
mid=3 · 7>6:nums[3]=7 > 6,r 移到 2。这时 l(3) 越过了 r(2),范围空了,循环结束,返回 -1。
只要数据有序(或答案有单调性),就该想二分:搜索插入位置、x 的平方根、爱吃香蕉的珂珂都是它的变形。
参考代码(Python)
1def search(self, nums, target):
2 left, right = 0, len(nums) - 1
3 while left <= right: # 注意等号
4 mid = left + (right - left) // 2 # 取中间,防溢出
5 if nums[mid] == target:
6 return mid # 命中
7 elif nums[mid] < target:
8 left = mid + 1 # 丢左半
9 else:
10 right = mid - 1 # 丢右半
11 return -1复杂度分析
- 时间复杂度:O(log n) —— 每次范围减半
- 空间复杂度:O(1) —— 只用几个指针
套路模板
三条铁律:while 带等号、mid 防溢出、边界 +1/-1。背下来基本不会错。
1l, r = 0, len(nums) - 1
2while l <= r:
3 mid = l + (r - l) // 2
4 if nums[mid] == target: return mid
5 elif nums[mid] < target: l = mid + 1
6 else: r = mid - 1
7return -1易错点
- ❌ while l < r(漏等号) → ✅ while l <= r(会漏掉区间只剩一个元素的情况)
- ❌ l = mid 或 r = mid → ✅ l = mid+1 / r = mid-1(不 +1/-1 会卡在原地死循环)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。