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

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

题目

已知一个长度为 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:

输入:nums = [1,3,5]
输出:1

示例 2:

输入:nums = [2,2,2,0,1]
输出:0

提示:

  • n == nums.length
  • 1 1 1 <= n <= 5000 5000 5000
  • − 5000 -5000 5000 <= nums[i] <= 5000 5000 5000
  • nums 原来是一个升序排序的数组,并进行了 1 1 1 n n n 次旋转

进阶:这道题与 寻找旋转排序数组中的最小值 类似,但 nums 可能包含重复元素。允许重复会影响算法的时间复杂度吗?会如何影响,为什么?

题解

方法一:二分查找

思路

​​  旋转排序数组 n u m s nums nums 可能 被拆分为 2 个排序数组 n u m s 1 , n u m s 2 nums1 , nums2 nums1,nums2,并且 n u m s 1 nums1 nums1 任一元素 > = n u m s 2 >= nums2 >=nums2 任一元素;因此,考虑二分法寻找此两数组的分界点 n u m s [ i ] nums[i] nums[i] (即第 2 个数组的首个元素)。
​​  设置 leftleft, rightright 指针在 numsnums 数组两端,midmid 为每次二分的中点:

  • n u m s [ m i d ] > n u m s [ r i g h t ] nums[mid] > nums[right] nums[mid]>nums[right] 时, m i d mid mid 一定在第 1 个排序数组中, i i i 一定满足 m i d < i < = r i g h t mid < i <= right mid<i<=right,因此执行 l e f t = m i d + 1 left = mid + 1 left=mid+1
  • n u m s [ m i d ] < n u m s [ r i g h t ] nums[mid] < nums[right] nums[mid]<nums[right] 时, m i d mid mid 一定在第 2 个排序数组中, i i i 一定满足 l e f t < i < = m i d left < i <= mid left<i<=mid,因此执行 r i g h t = m i d right = mid right=mid
  • n u m s [ m i d ] = = n u m s [ r i g h t ] nums[mid] == nums[right] nums[mid]==nums[right] 时,是此题对比 153题 的难点(原因是此题中数组的元素可重复,难以判断分界点 ii 指针区间);
    • 例如 [ 1 , 0 , 1 , 1 , 1 ] [1, 0, 1, 1, 1] [1,0,1,1,1] [ 1 , 1 , 1 , 0 , 1 ] [1, 1, 1, 0, 1] [1,1,1,0,1],在 l e f t = 0 , r i g h t = 4 , m i d = 2 left = 0, right = 4, mid = 2 left=0,right=4,mid=2 时,无法判断 m i d mid mid 在哪个排序数组中。
    • 我们采用使相同两端某一端移动下标直到新的 l l l r r r 的值不相等,来解决此问题(即恢复二分性),证明:
      • 此操作不会使数组越界:因为迭代条件保证了 right > left >= 0;
      • 此操作不会使最小值丢失:假设 nums[right]nums[right] 是最小值,有两种情况:
      • n u m s [ r i g h t ] nums[right] nums[right] 是唯一最小值:那就不可能满足判断条件 n u m s [ m i d ] = = n u m s [ r i g h t ] nums[mid] == nums[right] nums[mid]==nums[right],因为 mid < right(left != right 且 mid = (left + right) // 2 向下取整)

在这里插入图片描述

代码实现

Leetcode 官方题解:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low = 0;
        int high = nums.size() - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            }
            else if (nums[pivot] > nums[high]) {
                low = pivot + 1;
            }
            else {
                high -= 1;
            }
        }
        return nums[low];
    }
};

我自己的:

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        int l = 0, r = nums.size() - 1, mid;
        // 减去重复值,恢复二段性,但是不能全删除,要保留同样数字至少1个(和 81 题不一样)
        while(nums[l] == nums[r] && l < r) l++; // 避免[1,1]
        while(l <= r){
            mid = (l+r)/2;
            if(nums[l] <= nums[r]) break;
            else{
                if(nums[mid] >= nums[l]) l = mid + 1;   // 删除左区间
                else r = mid;   // 删除右侧区间
            }
        }
        return nums[l];
    }
};
复杂度分析

时间复杂度:恢复二段性处理中,最坏的情况下(考虑整个数组都是同一个数)复杂度是 O ( n ) O(n) O(n),而之后的找旋转点是「二分」,复杂度为 O ( l o g n ) O(log{n}) O(logn)。整体复杂度为 O ( n ) O(n) O(n) 的。
空间复杂度 O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值