题目描述
一个升序数组在某个点旋转过(比如 [0,1,2,4,5,6,7] 转成 [4,5,6,7,0,1,2]),在里面找 target,找到返回下标,没有返回 -1。
nums = [4,5,6,7,0,1,2]
target = 0
输出 = 4
思路解析
挨个找当然行,可那就白白浪费了「旋转前本来有序」这个条件。题目都把有序明牌摆你面前了,咱们必须把二分用起来。
不管从哪转的,把数组从 mid 切成两半,总有一半是完整升序的。先认出有序的那半,就好办了。
如果 target 落在「那个有序半」的范围内,就去那半找;否则去另一半。每一步照样砍掉一半。
l=0, r=6:mid=3 处是 7。左半 4..7 是有序的,但 0 不在 4 到 7 之间,所以答案在右半,l 跳到 4。
l=4, r=6:现在只看右段。mid=5 处是 1,它左边 0..1 有序,而 0 正好落在 0 到 1 之间,所以收右,r 收到 4。
l=4, r=4:区间只剩一个,mid=4 处正好是 0 = target。返回下标 4,搞定。
旋转数组的二分,核心就这一句:哪半有序看得出来(端点比一下),target 在不在那半也看得出来(范围比一下)。剩下交给二分。
参考代码(Python)
Python
1l, r = 0, len(nums) - 1 # 闭区间 [l, r]
2while l <= r:
3 mid = (l + r) // 2
4 if nums[mid] == target: return mid
5 if nums[l] <= nums[mid]: # 左半有序
6 if nums[l] <= target < nums[mid]: r = mid - 1
7 else: l = mid + 1
8 else: # 右半有序
9 if nums[mid] < target <= nums[r]: l = mid + 1
10 else: r = mid - 1
11return -1复杂度分析
- 时间复杂度:O(log n) —— 每轮砍一半
- 空间复杂度:O(1) —— 几个指针
套路模板
骨架记牢:先比 nums[l] 和 nums[mid] 定有序半,再用「闭区间范围」判 target 落哪半。LC81(含重复)、LC153(找最小)都是它的变体。
Python
1while l <= r:
2 mid = (l + r) // 2
3 if nums[mid] == target: return mid
4 if nums[l] <= nums[mid]: # 左半有序
5 if nums[l] <= target < nums[mid]: r = mid-1
6 else: l = mid+1
7 else: # 右半有序
8 if nums[mid] < target <= nums[r]: l = mid+1
9 else: r = mid-1易错点
- ❌ nums[l] < nums[mid] 判有序 → ✅ nums[l] <= nums[mid](mid 和 l 可能相邻甚至重合,必须带等号,否则漏判)
- ❌ target 范围判断用开区间 → ✅ 落点比较要把端点算进去(闭区间)(端点本身可能就是答案,漏了就找不到)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。