【剑指offer】旋转数组的最小数字

旋转数组的最小数字

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

题意

原数组为一个非递减的序列,将前若干个数字一起移动到末尾。给你一个移动后得到的数组,返回数组中最小的数。数组长度为0则返回0.
例如:
原数组为 [1,2,3,4,5]
将[1,2,3]移动到数组后得到旋转数组
[4,5,1,2,3]

一般思路

设一个变量mins存最小值,遍历一遍数组比较得到最小值,因此时间复杂度O(n)

方法伪代码

int mins = nums[0];
for(int i = 1; i < nums.size(); i ++ )
    mins = min(mins,nums[i]);
return mins;

二分思路

一般情况下可以做到O(logn),最坏情况下还是O(n)

一般情况分析

设原数组为[1,2,3,4,5],用图表示为,PS: n为数组最后一个数的下标
非递减序列数组

取前两个数即[1,2]移动到数组后面得到旋转数组[3,4,5,1,2],用图表示为
图片说明
我们的目的是要找到虚线右边的递增序列中最低点,由图可以得知nums[0]即3是大于虚线右边所有的数且在虚线左边的上升序列中为最低点,所以若nums[mid]<nums[0]就表示mid在虚线右边上升序列中,答案就在[l,mid]之间,反之mid在虚线左边的上升序列中,答案就在[mid + 1,r]。

方法伪代码

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];

数组中存在重复的数

图片说明
此时虚线左右两边有相等值,当nums[mid]落在这个值时就不能判断是落在虚线左边还是右边,将无法使用二分正确找到答案区间。
因此需要将虚线右边的横线即与nums[0]相等的值删除

int n = nums.size() - 1;
while(n > 0 && nums[n] == nums[0])
    n --;

此操作后就会变成一般情况,可以正常使用二分。

特殊情况

在上述去除相等值操作中存在一种特殊情况,虚线右边的数全部被删除。那这时候数组中最小的数就是nums[0],直接返回即可。

完整代码

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(!rotateArray.size())
            return 0;
        int n = rotateArray.size() - 1;
        while(n > 0 && rotateArray[n] == rotateArray[0])
            n --;
        if(rotateArray[n] >= rotateArray[0])
            return rotateArray[0];

        int l = 0, r = n;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(rotateArray[mid] < rotateArray[0])
                r = mid;
            else
                l = mid + 1;
        }
        return rotateArray[r];
    }
};
全部评论

相关推荐

头像
11-18 16:08
福州大学 Java
影流之主:干10年不被裁,我就能拿别人一年的钱了,日子有盼头了
点赞 评论 收藏
分享
M_bao:换个排版吧哥们,看着费劲
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务