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

旋转数组的最小数字

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);
    }
};
全部评论

相关推荐

01-01 10:21
门头沟学院 Java
谁懂啊!我实习遇到的公司,真的太把实习生当正式员工使唤了,刚入职没几天,连项目代码结构都没摸透,就被安排写项目了!一开始都是些接口对接、数据格式转换的基础活,听起来不难,但架不住我对项目的业务逻辑、代码规范一窍不通。对着前辈丢过来的需求文档,我一边查代码注释,一边翻技术文档,磕磕绊绊写完功能,也只知道&nbsp;“这么写能跑通”,根本不明白&nbsp;“为什么要这么设计”,妥妥的知其然不知其所以然。本以为这种基础活会干很久,结果没过多久,领导直接甩给我一个小功能的开发方案,让我负责从方案落地到功能对接、测试上线的全流程。当时我直接懵了,硬着头皮啃需求、画流程图、写核心代码,遇到不懂的就逮着前辈狂问,加班加点成了家常便饭。更没想到的是,后面居然让我独立负责一个模块的开发,还要做性能优化。从数据库索引调整,到接口响应速度提升,每一步都得自己琢磨、自己验证。那段时间真的累到飞起,每天下班脑子都是懵的尤其是发版的时候,我比谁都紧张,盯着监控屏大气不敢喘,生怕自己写的代码出&nbsp;bug&nbsp;导致系统崩溃。一旦出问题,就得立刻配合运维回滚版本,然后自己留下来加班排查修复,常常整栋办公楼只剩我一个人的工位亮着灯。每天加班到深夜,工作量比正式员工还饱和,我不止一次对着电脑发呆:我到底是来实习的,还是来打工的?虽然这段经历确实让我的技术能力突飞猛进,但那种被推着往前走的疲惫感,直到现在想起来都觉得累。
Toxic丶爵:感觉是好事,强度高就能学到更多
大家实习都在做什么?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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