题解 | #旋转数组的最小数字#

旋转数组的最小数字

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

这题常规使用二分查找来解,这里先皮一下,直接用set初始化函数排序,两行代码秒解。同理也可以直接用sort函数排序,也是两行解。

ps: 使用set函数初始化的运行速度和使用sort排序的时间基本一致,都是超过95%www。空间复杂度上当然sort更优。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        set<int> s(rotateArray.begin(),rotateArray.end());
        return *s.begin();
    }
};


言归正传,下面讲解一下二分查找的解法。
首先先从简单的情况出发,假设原数组中无重复数字,由题目知该数组为升序数组旋转后得到。那么该数组必定可以分为两部分,左边部分和右边部分都是一个升序数组,而其中间分隔处的点就是要寻找的最小值点。
那我们使用二分查找的条件就很明确,若左数left比中心数mid小,说明mid仍处在左边部分的升序数组中,使查找范围右缩,left = mid。当left大于等于mid时,若右数right比mid大,则说明mid处在右边部分的升序数组中,使查找范围左缩,right = mid。当以上条件均不满足,即left大于等于mid,right小于等于mid时,返回left和right中的小值,即为最小值。
二分查找的一个特点是,根据需求的不同,细节会发生许多变化,这里也一样。在基本的二分查找寻找目标数时,一般移动left与right是

left = mid + 1;
right = mid - 1

而本题中使用了

left = mid;
right = mid

原因是,寻找目标数中可以通过判断是否等于目标数,

target == arr.at[mid]

来将mid排除到查找范围之外,而本题中不行。本题中寻找的最小值是一个未知数,你无法判断当前的mid是否就是你要寻找的数,故不能将mid排除在搜索范围之外。

代码写到这里时,题目提供的两个测试用例都无事通过了,还是一样的剧本,自信满满点下提交,用例未通过光速吃瘪。
上面的代码忽略了一个重要的问题,重复数字。当数字重复时,上面的判断逻辑将被完全打乱。这也就是为什么会出现开头那个用set的构造函数解题的代码了w。
当我看到重复数的时候,第一反应就是去重,提到去重条件反射就会想到set。但是本题显然不能使用set来解,那就只能手动去重。
当重复数字位于数组中时,这显然不会干扰我们写好的判断逻辑,因为此时它们仍处在同一个升序数组中,没有改变我们前提假设的条件。
而当重复数字出现在数组的头部和尾部时,就可能出现问题,使得得判断逻辑出错。因为二分查找最开始是通过头尾元素得出mid,然后用mid与头尾元素进行比较,若left与right相等,则会直接跳出循环,返回一个错误的结果。所以需要对left进行优化,开始时比较left元素与队尾元素,若想等,则left++,直到不等或left=right。这样做只有两个结果,若最后left=right,则整个数组只有一个数字,返回任意一个数,若最后left!=right,则开头的重复元素已被去除,整体判断逻辑可以正常运行。
这里还有一种情况,就是数组是开头的重复元素,加上后面只有一个升序数组的结构。比如{3,3,1,2,3},这种情况也不符合我们提前假设好的情况,所以也需要排除。实际操作也比较简单,排除完开头的重复元素后,将left与队尾元素进行比较,若其比队危元素小,则直接返回left。因为我们知道,按照我们提前假设的条件,数组分为左右两个升序数组,那么左边是一定比右边大的,若左边比右边小了,那么显然它只有一个升序数组。

感觉自己这道题还没有吃透,欢迎大家一起讨论研究。

ps: 使用set构造函数,使用sort排序,以及使用下面这种二分查找的时间复杂度基本一致,都是超过95%。而空间复杂度上二分查找有绝对优势,set构造函数超过30%,sort超过50%,二分查找超过85%。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int left = 0;
        int right  = rotateArray.size() - 1;
        while(left < right && rotateArray.at(left) == rotateArray.back()){
            left++;
        }
        if(left == right){
            return rotateArray.at(left);
        }
        if(rotateArray.at(left) < rotateArray.at(right)){
            return rotateArray.at(left);
        }
        while(left < rotateArray.size() && right >= 0 && right > left){
            int mid = left + (right - left)/2;
            if(rotateArray.at(left) < rotateArray.at(mid)){
                left = mid;
            }
            else if(rotateArray.at(right) > rotateArray.at(mid)){
                right = mid;
            }
            else{
                break;
            }
        }
        return rotateArray.at(left) < rotateArray.at(right) ? rotateArray.at(left) : rotateArray.at(right);
    }
};
全部评论

相关推荐

只因飞飞:今日首绷
点赞 评论 收藏
分享
头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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