
数组特点:
- 原本升序
- 旋转后变成两段升序
- 最小值就是 两段的分界点
- 最小值在无序的那半边
例如:
coffeescript
[4,5,6,7,0,1,2]
↑
最小值
- 情况 1:nums[mid] > nums[right]:说明 mid 到 right 这一段是无序的(被断开),右半边无序,最小值在右半边
- 情况 2:nums[mid] < nums[right]:说明 mid 到 right 这一段是升序的,右半边有序,最小值在左半边
为什么只能和右端点比较?
假设遇到 nums[mid] > nums[left] 这个情况:
- 场景 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 的右边。
- 结论:同样的条件 nums[mid] > nums[left],最小值可能在左,也可能在右。算法无法做出决策。
这题中,我们不是找目标值,是找最小值,即:答案是有的,不需要返回 -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
在元素互不相同的前提下,nums[mid] == nums[right] 只有一种情况会发生:就是 mid == right。
由于 mid 是向下取整,mid == right 只会在 left == right 时发生。此时返回 ans 是正确的。但是,如果这道题稍微变一下,允许重复元素(比如 LeetCode 154 题) return ans 就会提前收工,导致错误。
用 while left <= right 时,当找到 nums[mid] == target 时返回mid坐标,找不到结束循环时,是left > right
- 内部必须有 if nums[mid] == 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。此时这个重合的点就是唯一没有被排除掉的点。