Leetcode 154. 寻找旋转排序数组中的最小值 II

1.题目基本信息

1.1.题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
  • 若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
    注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须尽可能减少整个过程的操作步骤。

1.2.题目地址

https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array-ii/description/

2.解题方法

2.1.解题思路

二分查找(红蓝染色法)

2.2.解题步骤

第一步,确定红蓝染色的两个特征。不变特征:1.红色始终标记在小于target的指针位置(target这里为目标求的最小索引),蓝色始终标记大于等于target的指针位置;2.左闭右闭,left-1始终指向红色,right始终指向蓝色(这里和普通的二分情况有所不同,right从一开始就属于蓝色阵营)

第二步,使用左闭右闭区间的模板处理。mid取中位数的下位整数,如果mid指向的值大于right指向的值,mid一定小于target,标记mid处为红色,即left更新为mid+1。如果小于right指向的值,则一定在右非递减区间,但是这里的right不能更新为mid-1,因为mid-1可能是红色区域,但是right不能指向红色区域,所以将right更新mid处。如果right和mid处值相等,此时mid可能是红色区域也可能是蓝色区域,所以right自减1是最保险的策略。最后直到left大于right退出循环。

第三步,经过上面的循环,由于left-1指向最后一个红色位置的特征,所以left一定指向target,返回nums[left]即为最终结果。

注意:这里如果nums[mid] < nums[right]的情况不好处理,可以去掉这一步的判断,在nums[mid] <= mid[right]时,直接right自减1,一样能AC,且结果差不了多少。这种做法可以参考下面的C++代码。

3.解题代码

Python代码

class Solution:
    # 特点:right处的值在右边的非递减区间总是最大的,但是比左边的非递减区间的值都要小。根据这个特征就可以进行二分法求解。
    # 第一步,确定红蓝染色的两个特征。不变特征:1.红色始终标记在小于target的指针位置(target这里为目标求的最小索引),蓝色始终标记大于等于target的指针位置;2.左闭右闭,left-1始终指向红色,right始终指向蓝色(这里和普通的二分情况有所不同,right从一开始就属于蓝色阵营)
    # 第二步,使用左闭右闭区间的模板处理。mid取中位数的下位整数,如果mid指向的值大于right指向的值,mid一定小于target,标记mid处为红色,即left更新为mid+1。如果小于right指向的值,则一定在右非递减区间,但是这里的right不能更新为mid-1,因为mid-1可能是红色区域,但是right不能指向红色区域,所以将right更新mid处。如果right和mid处值相等,此时mid可能是红色区域也可能是蓝色区域,所以right自减1是最保险的策略。最后直到left大于right退出循环。
    # 第三步,经过上面的循环,由于left-1指向最后一个红色位置的特征,所以left一定指向target,返回nums[left]即为最终结果。
    # 注意:这里如果nums[mid]<nums[right]的情况不好处理,可以去掉这一步的判断,在nums[mid]<=mid[right]时,直接right自减1,一样能AC,且结果差不了多少。
    def findMin(self, nums: List[int]) -> int:
        left,right=0,len(nums)-1
        while left<=right:
            mid=(right-left)//2+left
            if nums[mid]>nums[right]:
                left=mid+1
            elif nums[mid]<nums[right]:
                right=mid   # mid-1可能会跳到左非递减区间,所以不可以
            else:   # 相等的时候不能盲目二分,mid可能是红色阵营也可能是蓝色阵营,所以让right自减1即可
                right-=1
        return nums[left]

C++代码

class Solution {
public:
    int findMin(vector<int>& nums) {
        int length=nums.size();
        int left=0,right=length-1;
        while(left<right){
            int mid=(right-left)/2+left;
            if(nums[mid]>nums[right]){
                left=mid+1;
            }else{
                right--;
            }
        }
        return nums[left];
    }
};

4.执行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GEEK零零七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值