二分查找(2)

寻找旋转排序数组中的最小值(力扣153)

思路:通过对可能情况的分析,在寻找最小值时使用右边界作为判断条件,可以简化判断。这里使用右边界的情况在参考1中讲的非常清楚。我这里在做下说明:

如果mid < right,则最小值在左半边,可以收缩右边界。

如果mid > right,则最小值在右半边,可以收缩左边界。

通过比较mid与right,可以确定最小值的位置范围,从而决定边界收缩的方向。

而在将mid与left做比较时,left < mid,最小值可能在左半边,例如[12345]的left(=1)<mid(=3), 这时候需要缩小右边界。

也可能在右半边,例如[34512]的left(=3)<mid(=5)这时候需要缩小左边界。

这说明,如果只比较左值与中值,不能确定最小值的位置范围。

1
2
3
4
5
6
7
8
9
10
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1 # 左闭右闭区间,如果用右开区间则不方便判断右值
while left < right: # 循环不变式,如果left == right,则循环结束
mid = (left + right) >> 1 # 地板除,mid更靠近left
if nums[mid] > nums[right]: # 中值 > 右值,最小值在右半边,收缩左边界
left = mid + 1 # 因为中值 > 右值,中值肯定不是最小值,左边界可以跨过mid
elif nums[mid] < nums[right]: # 明确中值 < 右值,最小值在左半边,收缩右边界
right = mid # 因为中值 < 右值,中值也可能是最小值,右边界只能取到mid处
return nums[left] # 循环结束,left == right,最小值输出nums[left]或nums[right]均可

这里对边界的注释非常清晰,宗旨就是在缩小范围的同时不能丢掉的位置

寻找旋转排序数组中的最小值 II(力扣154)

思路:基本思路和上题相同,在相等时需要将右边界左移(这里不将左边界右移是因为单调递增的列表,如果列表未旋转会导致丢失结果)

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) >> 1
if nums[mid] > nums[right]:
left = mid + 1
elif nums[mid] < nums[right]:
right = mid
else:
right -= 1
return nums[left]

搜索二维矩阵(力扣74)

参考: