
数组特点:
- 原本升序
- 旋转后变成两段升序
- 最小值就是 两段的分界点
- 最小值在无序的那半边
例如:
coffeescript
[4,5,6,7,0,1,2]
↑
最小值
- 情况 1:numsmid > numsright:说明 mid 到 right 这一段是无序的(被断开),右半边无序,最小值在右半边
- 情况 2:numsmid < numsright:说明 mid 到 right 这一段是升序的,右半边有序,最小值在左半边
为什么只能和右端点比较?
假设遇到 numsmid > numsleft 这个情况:
- 场景 A(无旋转/完全升序):1, 2, 3, 4, 5 left=1, mid=3。此时 3>13 > 13>1,最小值在 mid 的左边
- 场景 B(有旋转):3, 4, 5, 1, 2 left=3, mid=5。此时 5>35 > 35>3,最小值在 mid 的右边。
- 结论:同样的条件 numsmid > numsleft,最小值可能在左,也可能在右。算法无法做出决策。
这题中,我们不是找目标值,是找最小值,即:答案是有的,不需要返回 -1,只需要找到位置
coffeescript
class Solution:
def findMin(self, nums: List[int]) -> int:
left = 0
right = len(nums)-1
ans = float("inf") # 无穷大
while left <= right:
mid = (left + right) // 2
# 没有target, 要找的是最小值,所以维护当前观察到的最小值
if nums[mid] < ans:
ans = nums[mid]
if nums[mid] > nums[right]: # 右边断层,在右边
left = mid + 1
elif nums[mid] < nums[right]: # 右边连续,在左边
right = mid - 1
else:
# nums[mid] == nums[right] ,此时结束循环
return ans
return ans
🚩 潜在的问题:else: return ans
在元素互不相同的前提下,numsmid == numsright 只有一种情况会发生:就是 mid == right。
由于 mid 是向下取整,mid == right 只会在 left == right 时发生。此时返回 ans 是正确的。但是,如果这道题稍微变一下,允许重复元素(比如 LeetCode 154 题) return ans 就会提前收工,导致错误。
用 while left <= right 时,当找到 nums[mid] == target 时返回mid坐标,找不到结束循环时,是left > right
- 内部必须有 if numsmid == target: return mid。
- 指针移动总是 left = mid + 1 和 right = mid - 1。
- 终局状态:如果循环是因为不满足条件而结束,此时 left > right(实际上此时 left = right + 1)
- 适用场景:在一个没有重复元素的有序数组中找一个具体的数
用 while left < right 时,当找到 nums[mid] == target 时,让循环继续,则结束循环时,是left == right
- 循环内部不直接返回,而是不断缩小 left, right 区间
- 指针移动通常是 left = mid + 1 和 right = mid
- 在 Python 中,mid = (left + right) // 2 是向下取整的。当区间只剩下两个元素(例如 left=0, right=1)时:mid = (0 + 1) // 2 = 0,此时 mid 永远等于 left。
- 如果写 right = mid - 1,就把这个潜在的正确答案彻底排除在搜索区间之外了。所以必须写 right = mid,确保正确答案依然留在 left, right 区间内。
- 终局状态:结束时 left == right。此时这个重合的点就是唯一没有被排除掉的点。