目录
题目链接
154 Find Minimum in Rotated Sorted Array II
题目描述
给定在某个旋转点被旋转后的非降序数组,数组中可能含有重复元素,返回数组中元素的最小值。
示例
输入:nums = [2,2,2,0,1]
输出:0
解决思路
这个题目是153. Find Minimum in Rotated Sorted Array的扩展,增加了“数组中可能有重复元素”这一特征。当旋转后的升序数组中没有重复元素时,如果nums[mid] > nums[right],说明mid应该在左排序数组[0,left),否则(即nums[mid] <= nums[right])说明mid应该在右排序数组[right,len(nums))。根据mid的位置去对left、right指针进行相应移动。
但是如果数组中包含重复元素,会发现如果nums[mid] > nums[right],mid在左排序数组;当nums[mid] < nums[right]时,mid在右排序数组;当nums[mid] = nums[right]时,无法判断mid在左排序数组还是在右排序数组。比如在[3,3,3,1,3]中,当left = 0,right = 4时,nums[mid] = nums[-1] = 3,此时nums[mid]属于左排序数组。而在[10,1,10,10,10]中,left = 0,right = len(nums) = 5时,nums[mid] = nums[-1] = 10,此时nums[mid]属于右排序数组。
我们可以这样处理:当nums[mid] == nums[right]时,令right -= 1。无论nums[right]是否是最小值,由于还有nums[mid],因此可以将right左移一位缩小搜索范围,同时不会影响到最小值的获取。
Python实现
class Solution:
def findMin(self, nums: List[int]) -> int:
left = 0
right = len(nums) -1
while(left<right):
mid = (left + right) // 2
if(nums[mid] > nums[right]): #mid在左侧排序数组
left = mid + 1
elif(nums[mid] < nums[right]): #mid 在右侧排序数组
right = mid
else:
right -= 1
return nums[left]
时间复杂度和空间复杂度
该算法的平均时间复杂度为O(logN),如果数组是随机生成的,那么数组中包含相同元素的概率很低,因此二分法在大部分情况下能够忽略一半的空间。如果数组中全是相同的元素,那么while循环会执行N次,时间复杂度会退化成O(N),这是最坏的情况。空间复杂度为O(1)。
进一步扩展
如果题目改为 “返回旋转点的索引”,那么上面的解决方案可能会出现right越过旋转点的情况。如[1,1,1,1,1,1,1,1,2,1,1]中用上面的实现方法求解时,初始值left = 0,right = 10,mid = 5,由于nums[mid] = nums[right],right更新为9;第二次循环left = 0,right = 9,mid = 4,由于nums[mid] = nums[right],right 更新为8。第三次循环left = 0,right = 8,mid = 4,nums[mid] < nums[right],right更新为4;第三次循环left = 0,right = 4,mid = 2,...... 可以看出在第二次循环中更新right时就已经将真正的旋转点nums[8]给越过了。
对于这个问题的解决方法是,在nums[mid] = nums[right]条件下,先检查nums[right]有没有可能是旋转点, 如果nums[right-1] > nums[right],则nums[right]为旋转点,直接返回。如果不是,再执行right -= 1。
代码实现如下:
class Solution:
def findMin(self, nums: List[int]) -> int:
left = 0
right = len(nums)-1
while(left<right):
mid = (left + right) // 2
if(nums[mid] > nums[right]): # mid在左侧排序数组
left = mid + 1
elif(nums[mid] < nums[right]): # mid在右侧排序数组
right = mid
else:
if(nums[right - 1] > nums[right]):
left = right
break
right -= 1
return left