寻找旋转数组的最小数字

这个是基础版
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

输入一个升序的数组的一个旋转,输出旋转数组的最小元素。

例如数组 {3,4,5,1,2} 为 {1,2,3,4,5} 的一个旋转,该数组的最小值为 1。

数组可能包含重复项。

注意:数组内所含元素非负,若数组大小为 0,请返回 −1。

样例
输入:nums = [2, 2, 2, 0, 1]

输出:0
看到这种题后,可以通过画图进行分析寻找规律。数组是递增的,旋转一般情况是从数组中间断开进行旋转,特殊情况是从数组末尾进行断开,并且断开位置前后的数字相同,此时没有后半段。
在这里插入图片描述
看到这后,发现前一段全部大于等于a[0],后一段全部小于等于a[0]。是时候想起来二分法了。
二分法的本质:存在性质的分界点,可以通过二分去寻找分界点,缩小范围,最后找到分界点。
但是此时还应该考虑刚才的特殊情况,如果二分的mid点落在与a[0]相等的那一段,此时无法区分是在前半段或者后半段。这样的话,我们可以二分之前把后半段与a[0】相等的那一段去掉,顺便判断从数组末尾进行断开,并且断开位置前后的数字相同,此时没有后半段的特殊情况。
这时直接开始代码

class Solution {
public:
    int findMin(vector<int>& nums) {
        if (nums.empty()) return -1;//数组为空时直接返回-1
        int  n = nums.size()-1;
        while (n>0&&nums[n]==nums[0]) n--;//消去后半段与a[0]相等的点
        if (nums[n]>nums[0]) return nums[0];//此时没有后半段,a[0] 最小
        int l = 0,r = n;
        while (l<r){
            int mid = l+r>>1;
            if (nums[mid]<nums[0]) r = mid;
            else
            l = mid+1;
        }
        return nums[r];
    }
};

发现有时候用二分算法的时候,会莫名奇妙的让数组陷入死循环,后来一想明白了,因为l,r最后都会变成mid, 既l,r相等,如果l!=mid+1,最后l,r之间剩余两个数的话就会陷入死循环。另外刚开始初始双指针对应左右区间的开闭情况也会有区别。
可以看看这个链接

力扣上有一道题搜索旋转排序数组 II
题目和上面这道题差不多,唯一的不同是要搜索这个旋转一次的数组里是否有某个值。
那这该怎样做呢?

上一道题我们可以找到数组中的最小值,即两端递增区间的分界点。 那想要查找某个值,得看看这个值在哪个区间里,那在这个区间里用二分查找不就OK了。顿时感觉这题,已经是成成的了。
上代码

class Solution {
public:
    bool check(vector<int>& nums, int l, int r, int target) {
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        return nums[r] == target;
    }

    bool search(vector<int>& nums, int target) {
        if (nums.empty()) return false;
        int n = nums.size() - 1;
        while (n > 0 && nums[n] == nums[0]) n -- ;
        if (nums[n] >= nums[0]) return check(nums, 0, n, target);//这是只有前一段,二分查找一段就行了
        int l = 0, r = n;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] < nums[0]) r = mid;
            else l = mid + 1;
        }

        if (target >= nums[0]) return check(nums, 0, r - 1, target);
        return check(nums, r, n, target);
    }
};

写完后一想不对劲,二分法在最坏的情况下还是O(n)的时间复杂度,那么我直接遍历一遍查找不也行!!!

class Solution {
public:
    bool search(vector<int>& nums, int target) {
            for (auto &e :nums){
                if (e==target)
                return true;
            }
            return false;
    }
};

。。。。。。。。。。。。。。麻了。。。。。

在4月8号,力扣又出了一道类似题
不过这道是可以进行多次旋转,并且,数组中的值并不允许重复,找出数组的最小值
链接在这
通过上面题的练习,通过分析,一般情况是,左右两段,最小值在右边那一段,直接二分。特殊情况是没有右边那一段,翻转多次正好使数组翻转回去,这时最小值为a[0],此时a[0]小于a[a.size()]。

class Solution {
public:
    int findMin(vector<int>& nums) {
            if (nums[0]<=nums.back()) return nums[0];
            int l = 0,r = nums.size()-1;
            while (l<r){
                int mid = l+r>>1;
                if (nums[mid]<nums[0])
                r = mid;
                else
                l = mid+1;
            }
            return nums[r];
    }
};

4月9号力扣每日一题还是关于旋转数组的,哈哈哈哈哈一看题,发现跟上面讲的第一题思路一样,直接把代码复制粘贴打卡成功。
4月9日每日一题
二分法例题:在D天内送达包裹的能力
剪绳子
旋转数组先介绍到这,后面慢慢更新

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值