剑指Offer---面试题8:旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。


思路1:如果此类题目出现在在线编程题中,那么想要通过系统的测试数据,无疑是非常简单的,不啰嗦,直接贴代码:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {

        //数组为空时
        if(rotateArray.size() == 0)
            return -1;
        //前部分数据旋转
        for(int i = 0; i < rotateArray.size() - 1; i++){
            if (rotateArray[i] > rotateArray[i + 1])
                return rotateArray[i + 1];
        }

        //全部数据旋转,相当于没有旋转,最小数即为第一个数
        return rotateArray[0];
    }
};

核心代码:第10行和第11行;旋转之后的数组由两个递增数组构成,如果发现了在原数组中a[i]>a[i+1],那么久可以判定最小值为a[i+1]了;
但是上述代码的时间复杂度为O(N),太慢,在面试的时候,如果给出上述实现,面试官必然不会满意!


思路2:
1. 分别用两个指针left,right指向数组的第一个元素和最后一个元素;(按题目中的规则,第一个元素应该是≥最后一个元素的)
2. 找到数组的中间元素center=(left+right)/2来取得;如果center位于前面的递增子序列,那么它应该≥left指向的元素;我们就可以将left移至center处,缩小寻找的范围;如果center位于后面的递增子序列,那么它应该≤right指向的元素;我们就可以将right移至center处,缩小寻找的范围;无论是移动哪一个指针,都会缩小范围,然后用更新之后的指针做新一轮的查找;
3. 最终第一个left指向前面子数组的最后一个元素,right指向后面子数组的第一个元素,也就是他们两指向相邻的两个元素,二第二个指针指向的刚好就是最小的元素.这就是循环的出口;


贴代码:

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array==null){
            throw new NullPointerException();
        }

        int length=array.length;//数组的长度
        int left=0;
        int right=length-1;
        int center=left;
        while(array[left]>=array[right]){
            if(right-left==1){
                center=right;
                break;
            }

            center=(left+right)/2;
            if(array[center]>=array[left]){
                left=center;
            }else if(array[center]<=array[right]){
                right=center;
            }
        }

       return array[center];

    }
}

注意:第一个数字总是大于最后一个数字,那么循环的条件就可以设置为while(array[left]>=array[right]),但是有一个特例,也就是当把排序数组的前面0个元素搬到后面的时候,那么旋转数组就等于原数组,则此时上述条件是不成立的,所以应该直接return[0] ;这也就是为什么int center=left;的原因(此时center==0)!

上诉代码时间复杂度:O(logN);


万事大吉啦? .. 别高兴得太早…

考虑如下情况:

[1,0,1,1,1]和[1,1,1,0,1]都可以看做是数组[0,1,1,1,1]的旋转;

在第一个旋转数组中,array[left]=1;array[right]=1;array[center]=1;
在第二个旋转数组中:也有
array[left]=1;array[right]=1;array[center]=1;
但是在第一种情况中,a[center]位于后面的子数组中;在第二种情况中,a[center]位于前面的数组中;

所以当array[left]==array[right]&&array[left]==array[center]&&array[right]==array[center]时,无法判断a[center]是位于前面的数组中还是后面的数组中,故无法移动指针,进而无法缩小范围.

当出现这种情况的时候,我们采用顺序查找来解决问题.

最终的代码如下所示:

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array==null){
            throw new NullPointerException();
        }

        int length=array.length;//数组的长度
        int left=0;
        int right=length-1;
        int center=left;
        while(array[left]>=array[right]){
            if(right-left==1){
                center=right;
                break;
            }
            //当下标left,right,center三个位置处的数相等的时候,则只能顺序查找
            if(array[left]==array[right]&&array[left]==array[center]&&array[right]==array[center]){
                return minInOrder(array,left,right);
            }
            center=(left+right)/2;
            if(array[center]>=array[left]){
                left=center;
            }else if(array[center]<=array[right]){
                right=center;
            }
        }

       return array[center];

    }

    int minInOrder(int[]array,int left,int right){
        int result=array[left];
        for(int i=left+1;i<=right;i++){
            if(result>array[i]){
                result=array[i];
            }
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值