剑指Offer11.旋转数组的最小数字

  • 剑指Offer11.旋转数组的最小数字

  • 题目:
    找出一个旋转数组numbers的最小值;
    有重复元素;
    在这里插入图片描述

  • 思路:
    1.二分: 时间O(log N) : 在最坏情况下会退化到 O(N)例如 [1, 1, 1, 1],就是因为本题有重复元素,只能将通常情况下优化为logn;空间O(1) : 变量使用常数大小的额外空间;

虽数组整体并不是有序的,但前后两部分都是递增的,因此仍可以用二分;
二分的核心:根据每次numbers [ mid ] 的值,必须确定目标值(即本题的最小值)在mid的哪一侧;
我们只关注整个旋转数组中的最小元素和最后一个元素numbers[ r ]:最小值右侧的元素都<=x,最小值左侧的元素>=x。因此若numbers [ mid ] > numbers [ r ] ,则mid一定在最小值左侧;若numbers [ mid ] < numbers [ r ] ,则mid一定在最小值右侧;若numbers [ mid ] == numbers [ r ] ,则mid在最小值的左右两侧都有可能(因为本题有重复元素),但二分必须要不断缩小区间,可以只把 right 舍弃掉,因为并不影响结果;
在这里插入图片描述

class Solution {
public:
    int minArray(vector<int>& numbers) {
       if (numbers.empty()) return INT32_MIN; //判空
       int len = numbers.size();
       if (numbers[0] < numbers[len - 1]) return numbers[0]; //未旋转
        
       int l = 0, r = len - 1;
       //最坏情况[2,2,2,2,2],每次都进入r--分支,相当于线性遍历
       while (l <= r) {
           int mid = l + ((r - l) >> 2);
           if (numbers[mid] > numbers[r]) l = mid + 1; //mid在最小值左侧,可以把mid左侧舍弃
           else if (numbers[mid] < numbers[r]) r = mid; //mid在最小值右侧,可以把mid右侧舍弃
           else r--; //mid不确定在最小值哪一侧,但为了缩小区间,可以把r所在的位置舍弃
       }
       //如果循环条件为l < r,则出循环时一定l==r,因此return numbers[l]和numsber[r]均可
       //如果循环条件为l <= r,则循环的最后一步由于r--导致l>r,此时最小值在下标l上
       return numbers[l];
    }
};

2.yxc的题解

class Solution {
public:
    int minArray(vector<int>& numbers) {
        if (numbers.empty()) return -1;
        int n = numbers.size() - 1;
        if (numbers[0] < numbers[n]) return numbers[0];//未旋转
        
        while (n > 0 && numbers[0] == numbers[n]) --n; //把后半部分末尾的等于numebrs[0]的值去掉,以便二分
        if (numbers[0] < numbers[n]) return numbers[0]; 
        
        int l = 0, r = n;//二分去找右半部分中,第一个<numbers[0]的数就是最小值;
        while (l < r) {
            int mid = l + ((r - l) >> 1);
            if (numbers[mid] >= numbers[0]) l = mid + 1;
            else r = mid;
        }
        return numbers[l];
    }
};
  • 总结:
    二分尽管道理简单,实现真的很容易错:l < r这个地方,单层循环中更新l和r时,返回l处还是r处的值,都好容易错,每道题的case都不同需要考虑清楚;
    mid和r比较而不是l的原因:若每次将 nums[ mid ] 与 nums[ left ]比较,无法确定最小值在mid左边还是右边:例:[3, 4, 5, 1, 2] 或 [1, 2, 3, 4, 5] ;每次将nums[ mid ] 与 nums[ right ]比较,可以确定最小值所在的目标区间:例:[1, 2, 3, 4, 5] 或 [3, 4, 5, 1, 2] 或 [2, 3, 4, 5 ,1];

方法2中第一个while处容易漏写n>0,注意不是n>=0
需要再刷;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值