天天看点

剑指offer面试题[8]-旋转数组的最小数字

题目描述

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

牛客网上给的函数接口: class Solution {

public:

    int minNumberInRotateArray(vector<int> rotateArray) {

} }

分析:        看到题目的第一刻,我想的是既然这个数组原本是非递减排序的,那么在其旋转后,就分为了两个非递减的子序列,第一个子序列的值一定是大于或等于第二个子序列的,那么我们可以从第一元素开始往后遍历,只要前面的元素比它后面的一个元素大,说明第一个子序列的最后一个元素和第二个子序列的第一个元素相遇了,那么明显当前元素后面的那个元素就是最小元素了。当然还要判断若数组元素是否为空。        于是我开始动手写程序,如下:  class Solution {

 public:

    int minNumberInRotateArray(vector<int> rotateArray) {         int n=rotateArray.size( );

        if(n==0)

              return 0;

        for(int i=0;i<n-1;i++)

          {

              if(rotateArray[i]>rotateArray[i+1])

                  return  rotateArray[i+1];

          }

        return  rotateArray[0];

} }        很神奇的是竟然通过了,我很兴奋,但是总觉得剑指offer上的题考虑的远远不止这,于是我打开课本,果然我很low,考虑的问题太少,复杂度是O(n)就不说,有的测试用例也没想到。              书上写的是利用 二分查找的思想(复杂度为O(logn)) 来解决这个问题,因为前面说过对于一个旋转的数组来说,旋转后数组实际上分成了两个排序的子数组,而且前面的子数组的元素均大于后面子数组的元素。而且最小的元素刚好是两个子数组的分界。        对于一个长度为n的数组,设置两个指针index1和index2,开始时index1指向第一个数组元素,即下标为0的位置,index2指向数组最后一个元素,及下标为n-1的位置。如果第一个元素大于最后一个元素,说明旋转了0个元素,输出第一个元素(最小)。否则找到数组中间位置indexMid的元素。indexMid=(index1+index2)/2。如果indexMid位置对应的值大于index1对应的值,那么说明indexMid位置对应的值位于第一个子数组中,最小值应该在indexMid后面,所以将indexMid赋给index1。如果indexMid位置对应的值小于index2对应的值,那么说明indexMid位置对应的值位于第二个子数组中,最小值应该在indexMid前面,所以将indexMid赋给index2。直到index1和index2的值相差1,即index1指向第一个子数组的最后一个位置,index2指向第二个子数组的第一个位置。读者可以自己以数组{3,4,5,1,2}分析。         于是有代码:(红色代码部分说明见最下面) class Solution {

public:

    int minNumberInRotateArray(vector<int> rotateArray) {

        int n=rotateArray.size( );

        if(n==0)

           return 0;

        int index1=0;

        int index2=n-1;

        int indexMid=index1;   

        while(rotateArray[index1]>=rotateArray[index2])

          {

             if(index2-index1==1)    //注意不要搞反了减数和被减数

                 {

                    indexMid=index2;

                    break;

                 }   

             indexMid=(index1+index2)/2;

             //如果出现了下标index1、index2及indexMid三个值相等的情况,那么就只能是顺序查找了

             if(rotateArray[indexMid]==rotateArray[index1]&&rotateArray[index1]==rotateArray[index2])

                 {

                    return MinInOrder(rotateArray,index1,index2);

                 }

             if(rotateArray[indexMid]>=rotateArray[index1])

                    index1=indexMid;

             else if(rotateArray[indexMid]<=rotateArray[index2])

                    index2=indexMid;

          }

        return rotateArray[indexMid];

    }        int MinInOrder(vector<int> rotateArray,int index1,int index2)     //注意函数定义的位置,不要在函数minNumberInRotateArray内定义

           {

              int Min=rotateArray[index1];

              for(int i=index1+1;i<=index2;i++)

                 { if(Min>rotateArray[i])

                      Min=rotateArray[i];

                 }

              return Min;

           }

};

       我们没有考虑到的一种情况是:如果index1、index2及indexMid对应的值相等怎么办?        比如数组{1,0,1,1,1}和数组{1,1,1,0,1}都是原始非递减数组{0,1,1,1,1}的旋转数组。可以看到第一个元素、第三个元素以及第五个元素的值都是1,此时, 我们不得不采用顺序查找的方法 。

继续阅读