153. 寻找旋转排序数组中的最小值
问题链接
https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/description/
解题思路
这个题目要求用logn的算法,那只能是二分了。
二分的时候,我们要讲究策略。首先,我们要找旋转数组中的最小值。我们先分析数据:
对于一般性的情况,我们是分为了2个上升数组,我们要找的元素是第二个数组的第一个元素。
对于极端情况,只有1个旋转数组,我们要找的元素是上升数组的第一个元素。
即,我们可以对下标进行二分。如果我们发现,一个元素满足它小于右边相邻的元素且小于左边相邻的元素(如果有的话),那么我们就找到了它。
如果不满足的话,就要看此时是左侧有序还是右侧有序。
如果左侧有序的话,那么我们直接drop掉(注意连带mid,因为我们要找的元素肯定不在第一个序列中),如果右侧有序的话,我们也可以直接drop掉,因为分界点肯定不在这里,因为它没有通过我们上面的检测。
代码
class Solution: def findMin(self, nums: List[int]) -> int: left_val, right_val = nums[0], nums[-1] left, right = 0, len(nums)-1 split_idx = 0 while left <= right: mid = (left+right)>>1 flag = False if mid-1>=0: if nums[mid-1]>nums[mid]: flag = True else: flag = False if mid+1<len(nums): if nums[mid]<nums[mid+1]: flag = flag and True else: flag = flag and False if flag: split_idx = mid break elif left_val <= nums[mid]: left = mid + 1 else: right = mid - 1 return nums[split_idx]
其实还有种更简单的思路。就是,如果我们在二分时,发现mid处于第二个序列,那么就将mid直接等于right,而不是等于right-1。这样的话可以保证我们不会把第二个序列直接drop掉(因为我们要找的是第二个序列的第一个元素),如果第二个序列有剩余,那肯定包含了我们要找的元素。
如果mid处于第一个序列,那直接mid=left+1。因为我们要找的元素肯定不会在第一个序列中。(但是我们无法判断第二个我们当前整体有几个序列,所以用上面那条规则即可)
另外,我们这是找最小值,保证可以找到。所以肯定不会出现left>right的情况。为了避免死循环,循环条件应为left<right。
所以更简洁的代码如下:
class Solution: def findMin(self, nums: List[int]) -> int: left_val, right_val = nums[0],nums[-1] left, right = 0, len(nums)-1 while left < right: mid = (left+right)>>1 if nums[mid] < right_val: right = mid else: left = mid + 1 return nums[left]
更优雅的方式:
其实本质上就是寻找border的过程。很简单的道理,大于等于最左边,肯定在左侧有序序列,应该往右找,否则往左找。
class Solution: def findMin(self, nums) -> int: left, right = 0, len(nums)-1 while left <= right: mid = (left + right) >> 1 if nums[mid] >= nums[0]: left = mid + 1 else: right = mid - 1 return nums[left] if left < len(nums) else nums[0]