1. 程式人生 > >【劍指Offer】6、旋轉數組的最小數字

【劍指Offer】6、旋轉數組的最小數字

length num 旋轉 是的 一次 排序數組 ray 技術 處理

??題目描述:

??把一個數組最開始的若幹個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。 NOTE:給出的所有元素都大於0,若數組大小為0,請返回0。

??解題思路:

??本題的直觀解法很簡單,直接對數組進行一次遍歷就可以找到最小值,復雜度為O(n),但是顯然這不是本題的意圖所在,因為沒有利用到任何旋轉數組的特性。

??進一步分析,如果整個數組是有序的,那我們一定會想到用折半查找來實現。對於旋轉數組,我們發現,它實際上可以劃分為兩個排序的子數組,而且前面數組的元素都不小於後面數組的元素,並且最小值正好就是這兩個數組的分界線,由此,我們可以得出以下解決方法。

??首先用兩個指針low和high分別指向數組的第一個元素和最後一個元素,然後可以找到中間元素mid。對於這個中間元素,有以下兩種情況:(1)該元素大於等於low指向的元素,此時最小的元素說明在mid的後面,可以把low=mid;(2)中間元素小於等於high指向的元素,那麽最小元素在mid之前,可以high=mid。特別註意:這裏不要+1或者-1,因為只有這樣才能保證low始終在第一個數組,high始終在第二個數組。依次循環,當最後low和high相差1時,low指向第一個數組的最後一個,high指向第二個數組的第一個(即為我們要找的最小值)。

??很明顯,以上查找的時間復雜度為O(logN)。


技術分享圖片

??除此之外,本題還有兩個特殊情況:

  • 將數組前0個元素移動到後面(相當於沒有旋轉,數組整體有序)。明顯我們上面的分析沒有包含這種情況,需要特殊處理,方法也很簡單,將第一個元素和最後一個元素相比,若第一個元素小於最後一個元素,則說明最小值就是的第一個元素,可以直接返回。

  • 首尾指針指向的數字和中間元素三者都相等時,無法判斷中間元素位於哪個子數組,無法縮小問題規模。此時,只能退而求其次,進行順序查找。

    技術分享圖片

??編程實現(Java):

    public int minNumberInRotateArray(int [] array) {
        /*
        三種情況:
        (1)把前面0個元素搬到末尾,也就是排序數組本身,第一個就是最小值
        (2)一般情況二分查找,當high-low=1時,high就是最小值
        (3)如果首尾元素和中間元素都相等時,只能順序查找
        */
        int len=array.length;
        if(len==0)
            return 0;
        int low=0,high=len-1;
        if(array[low]<array[high]) //排序數組本身
            return array[low];
        while(low<high){
            int mid=low+(high-low)/2;
            if(array[low]==array[mid] && array[high]==array[mid])
                return minInOrder(array);
            if(array[mid]>=array[low])
                low=mid;
            else if(array[mid]<=array[high])
                high=mid;
            if(high-low==1)
                return array[high];
        }
        return -1;
    }
    public int minInOrder(int [] array) { //順序查找
         int min=array[0];
         for(int num:array){
             if(num<min)
                 min=num;
         }
          return min;
     }

【劍指Offer】6、旋轉數組的最小數字