【leetcode-Python】- 二分搜索 - 154 Find Minimum in Rotated Sorted Array II

 

目录

 

题目链接

题目描述

示例

解决思路

Python实现

时间复杂度和空间复杂度

进一步扩展


题目链接

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值