旋转数组的最小数字 - 题解 - 二分查找

旋转数组的最小数字

http://www.nowcoder.com/questionTerminal/9f3231a991af4f55b95579b44b7a01ba

题目大意

一个非递减的排序数组,从某个地方进行旋转,要找到原数组中最小的元素。

解法

几个月前写了一个题解,被其他同学找出了很多漏洞,当时考虑问题不够全面,另外牛客网上的测试用例也比较少,很多错误没有被发现,这次根据评论区的反馈,做了一个彻底的修改。

首先举两个例子,下图中左边两幅是非递减数组绘制的图像,右边是旋转后绘制的图像。可以看出,旋转后右侧一定是小于等于左侧的。原数组中最小的值,其实就是右半边的左端点。

既然是排序后的数组,虽然经过了旋转,但是直觉还是告诉我要使用二分查找。二分查找涉及到三个量 lo, hi,mid。我们的目标是找到右边部分的左端点。

如果 mid 落在左半边,那么 lo=mid+1,否则设置 hi=mid。这样不断缩小范围,最终就能找到这个最低点。

那么如何确定 mid 落在那边呢,由于左半边一定大于等于右半边,因此可以使用 nums[mid] 和左半段左端点比较,如果落在左半段,那么 nums[mid] >= nums[0]。但是落在右半段,也有可能 nums[mid] == nums[0] 啊,因为右侧的右端点可能和左侧左端点的值相同。

因此我们需要保证左半部分一定大于右半部分,为此,我们只需要做如下操作:

while(nums[lo] == nums.back()){
    lo ++;
}

处理完成后,我们在 [lo,hi) 上寻找最小值。若 nums[mid] >= nums[lo] 那么 mid 落在左半部分,否则落在右侧。

经过一些朋友的提醒下,我发现还存在其他一些特殊情况:

特殊情况一:如果数组中所有元素的值都相同,那么 lo 就会一直增加,最终越界。

特殊情况二:如果左右两边相等的元素数量相同,那么循环完毕后 lo 就会是右半边的左端点。此时 [lo, hi) 所指的区间是递增区间,前面提到的二分查找策略此时就失效了。

下面是综合以上分析给出的解答:

class Solution {
public:
    int minNumberInRotateArray(vector<int> nums) {
        int lo = 0, hi = nums.size();

        while(lo < hi && nums[lo] == nums.back()){
            lo ++;
        }
        // 特殊情况一,所有元素都相等
        if(lo == hi){
            return nums[0];
        }
        // 特殊情况二
        if(nums[lo] < nums.back()){
            return nums[lo];
        }

        int left_min = nums[lo];
        while(lo < hi){
            int mid = lo + (hi - lo) / 2;
            if(nums[mid] >= left_min){
                lo = mid + 1;
            }else{
                hi = mid;
            }
        }
        return nums[lo];
    }
};
全部评论
并不易懂啊 。。。
3 回复 分享
发布于 2020-03-01 21:18
非递减的意思包含数组中的元素全相等,当数组元素全相等时会出现非法索引,应该加一句 while(a[lo] == a[hi]){ ++lo; --hi; if(lo >= hi)return a[0]; }
2 回复 分享
发布于 2020-02-12 00:36
back是什么,end?
点赞 回复 分享
发布于 2020-12-26 09:49
好像有漏洞,一直删之一步如果最后指针重合也会删然后指针还自加自减答案不对,或者应该加个重合前跳出的判断
点赞 回复 分享
发布于 2020-04-23 18:17
用例卡的太low了。 1 1 1 1 1 1 1 直接RE吧。
点赞 回复 分享
发布于 2020-04-21 16:14
分析的很清晰,就是感觉代码有一点点问题,万一是对称的情况{2,2,1,1,2,2},while{a[lo]==a[hi]}就会报错
点赞 回复 分享
发布于 2020-04-01 23:17
这个答案错了吧,关键牛客还给通过了。。。{1,2,3,4,5,1}
点赞 回复 分享
发布于 2020-03-26 00:28

相关推荐

面试官全程关摄像头1.自我介绍一下2.React和Vue哪个更熟悉一点3.你在之前那段实习经历中有没有什么技术性的突破(我只是实习了44天工作28天,我把我能说的都说了)4.你封装的哪个表单组件支不支持动态传值5.自己在实习阶段Vue3项目封装过hook吗6.hook有什么作用7.Vue2和Vue3的响应式区别(我说一个是proxy是拦截所有的底层操作,Object.defineProperty本身就是一个底层操作,有些东西拦截不了,比如数组的一些操作还有等等,面试官就说实在要拦截能不能拦截????我心想肯定不行呀,他的底层机制就不允许吧)8.pinia和vuex的区别(这个回答不出来是我太久没用了)9.pinia和zustand的区别,怎么选(直接给我干懵了)(我说react能用pinia吗&nbsp;&nbsp;他说要用的话也可以)10.渲染一万条数据,怎么解决页面卡顿问题(我说分页、监听滚轮动态加载,纯数据展示好像还可以用canvas画)(估计是没说虚拟表单,感觉不满意)11.type和interface的区别12.ts的泛型有哪些作用(我就说了一个结构相同但是类型不同的时候可以用,比如请求响应的接口,每次的data不同,这里能用一个泛型,他问我还有什么)13.你项目用的是React,如果让你再写一遍你会选择什么14.pnpm、npm、yarn的区别15.dependencies和devdependencies的区别总而言之太久没面试了,上一段实习的面试js问了很多。结果这次js一点没问,网络方面也没考,表现得很一般,但是知道自己的问题了&nbsp;&nbsp;好好准备,等待明天的影石360和周四的腾讯了&nbsp;&nbsp;加油!!!
解zj:大三的第一段面试居然是这样的结局
查看15道真题和解析
点赞 评论 收藏
分享
评论
47
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务