劍指Offer(java版):旋轉數組的最小數字

題目: 把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲旋轉。 輸入一個遞增的排序的數組的一個旋轉,輸出旋轉數組的最小元素。面試

 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小元素爲1.數組

這道題最直觀的解法並不難,從頭至尾遍歷一次,咱們就能找到最小的元素。這種思路的時間複雜度爲O(n)。可是這個思路沒有利用輸入的旋轉數組的特性,確定達不到面試官的要求。.net

咱們注意到旋轉以後的數組實際上能夠劃分爲兩個排序的子數組,並且前 面的子數組的元素都是大於或者等於後面子數組的元素。咱們還注意到最小的元素恰好是這兩個子數組的分界線。在排序的數組中咱們能夠利用二分查找來實現 O(logn)的查找。本題給出的數組在必定程度上是排序的,所以咱們能夠試着用二分查找的思路來尋找這個最小的元素。指針

之前面的例子爲例,咱們先把第一個指針指向第0個元素,把第二個指針 指向第4個元素,如圖所示。位於兩個指針中間(在數組的下標是2)的數字是5,它大於第一個指針指向的數字。所以中間數字5必定位於第一個遞增字數組中, 而且最小的數字必定位於它的後面。所以咱們能夠移動第一個指針讓它指向數組的中間。排序

此時位於這兩個指針中間的數字爲1,它小於第二個指針指向的數字。所以這個中間數字爲1必定位於第二個遞增子數組中,而且最小的數字必定位於它的前面或者它本身就是最小的數字。所以咱們能夠移動第二個指針指向兩個指針中間的元素即下標爲3的元素。索引

此時兩個指針的距離爲1,代表第一個指針已經指向了第一個遞增子數組的末尾,而第二個指針指向第二個遞增子數組的開頭。第二個子數組的第一個數字就是最小的數字,所以第二個指針指向的數字就是咱們查找的結果。get

上述方法是否就必定夠完美了呢?面試官會告訴你其實否則。他將提示咱們再仔細分析小標leftIndex和rightIndex分別和途中P1和 P2相對應)的兩個數相同的狀況。在前面,當着兩個數相同,而且它們中間的數相同的也相同時,咱們把IndexMid賦給了leftIndex,也就是認 爲此時最小的數字位於中間數字的後面。是否是必定同樣?class

咱們再來看一個例子。數組{1,0,1,1,1}和數組{1,1,1,0,1}均可以堪稱遞增排序數組{0,1,1,1,1}的旋轉,圖2分別畫出它們由最小數字分隔開的兩個子數組。List

這兩種狀況中,第一個指針和第二個指針指向的數字都是1,而且兩個指 針中間的數字也是1,這3個數字相同。在第一種狀況中,中間數字(下標爲2)位於後面是子數組;在第二種狀況中,中間數字(下標爲2)位於前面的子數組 中。所以,當兩個指針指向的數字及它們中間的數字三者相同的時候,咱們沒法判斷中間的數字是位於前面的子數組中仍是後面的子數組中國,也沒法移動兩個指針 來縮小查找的範圍。此時,咱們不得不採用順序查找的方法。遍歷

在把問題分析清楚後造成清晰的思路以後,咱們就能夠把前面的代碼修改成:

Java代碼實現:

 

package cglib;

public class List1
{  
     public static int minInReversingList(int[] arr){  
            if(arr==null){
                 System.out.println("空數組");  
                return -1;  
            }  
            int leftIndex = 0;//3,4,5,1,2  
            int rightIndex = arr.length -1; //長度5,序號4
            int midIndex = leftIndex;  //將中間數索引初始化爲第一個數索引,對應旋轉後仍然是排序數組的狀況,如旋轉後仍舊是1,2,3,4,5
            while(arr[leftIndex]>= arr[rightIndex]){
                System.out.println("左索引數大於等於右索引數");
                System.out.println("arr[leftIndex]="+arr[leftIndex]);
                System.out.println("arr[rightIndex]="+arr[rightIndex]);
                if(rightIndex - leftIndex <= 1){
                    System.out.println("rightIndex="+rightIndex);
                    System.out.println("leftIndex="+leftIndex);
                    midIndex = rightIndex;  
                    System.out.println("midIndex="+midIndex);
                    break;  
                }  
                midIndex = (leftIndex+rightIndex)/2;  
                if(arr[leftIndex]== arr[rightIndex] && arr[midIndex]== arr[leftIndex]){  
                    System.out.println("只能順序查找");
                    return MinInOrder(arr,leftIndex,rightIndex); //這三個數相同,無法分辨中間數屬於左右哪一個數組,只能按順序查找,找出最小值
                }  
                if(arr[midIndex] >= arr[leftIndex]){  
                    leftIndex = midIndex;  //3,4,5, 1,2.中間數大於最左邊的數,說明最小數在中間數右邊,左邊索引就變成中間數索引
                }else if(arr[midIndex] < arr[rightIndex]){  
                    rightIndex = midIndex;  //5,1,2,3,4.中間數小於最右邊的數,說明最小數在中間數左邊,右邊索引就變成中間數索引
                }  
            }  
            return arr[midIndex];  
        }  
        public static int MinInOrder(int[] arr,int leftIndex,int rightIndex){  
            int result = arr[leftIndex]; //1,0,1,1,1
            System.out.println("result="+result);
            System.out.println("rightIndex="+rightIndex);
            System.out.println("arr[leftIndex]="+arr[leftIndex]);
            for(int i = leftIndex +1;i<rightIndex;i++){  
                if(result> arr[i]){  
                    result = arr[i];  
                }  
            }  
            return result;  
        }  
        public static void main(String[] args){  
            
            int[] arr={3,4,5,1,2};
            //int[] arr={1,2,3,4,5};
            //int[] arr=null;
            //int[] arr={1,0,1,1,1};//{2,2,2,2,2,0,1,2,2};  
            System.out.println(minInReversingList(arr));  
              
        }
}

輸出:

左索引數大於等於右索引數 arr[leftIndex]=3 arr[rightIndex]=2 左索引數大於等於右索引數 arr[leftIndex]=5 arr[rightIndex]=2 左索引數大於等於右索引數 arr[leftIndex]=5 arr[rightIndex]=1 rightIndex=3 leftIndex=2 midIndex=3 1

相關文章
相關標籤/搜索