(轉)旋轉陣列的最小數字
題目: 把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為旋轉。 輸入一個遞增的排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。
例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小元素為1.
這道題最直觀的解法並不難,從頭到尾遍歷一次,我們就能找到最小的元素。這種思路的時間複雜度為O(n)。但是這個思路沒有利用輸入的旋轉陣列的特性,肯定達不到面試官的要求。
我們注意到旋轉之後的陣列實際上可以劃分為兩個排序的子陣列,而且前面的子陣列的元素都是大於或者等於後面子陣列的元素。我們還注意到最小的元素剛好是這兩個子陣列的分界線。在排序的陣列中我們可以利用二分查詢來實現O(logn)的查詢。本題給出的陣列在一定程度上是排序的,因此我們可以試著用二分查詢的思路來尋找這個最小的元素。
以前面的例子為例,我們先把第一個指標指向第0個元素,把第二個指標指向第4個元素,如圖所示。位於兩個指標中間(在陣列的下標是2)的數字是5,它大於第一個指標指向的數字。因此中間數字5一定位於第一個遞增字陣列中,並且最小的數字一定位於它的後面。因此我們可以移動第一個指標讓它指向陣列的中間。
此時位於這兩個指標中間的數字為1,它小於第二個指標指向的數字。因此這個中間數字為1一定位於第二個遞增子陣列中,並且最小的數字一定位於它的前面或者它自己就是最小的數字。因此我們可以移動第二個指標指向兩個指標中間的元素即下標為3的元素。
此時兩個指標的距離為1,表明第一個指標已經指向了第一個遞增子陣列的末尾,而第二個指標指向第二個遞增子陣列的開頭。第二個子陣列的第一個數字就是最小的數字,因此第二個指標指向的數字就是我們查詢的結果。
上述方法是否就一定夠完美了呢?面試官會告訴你其實不然。他將提示我們再仔細分析小標leftIndex和rightIndex分別和途中P1和P2相對應)的兩個數相同的情況。在前面,當著兩個數相同,並且它們中間的數相同的也相同時,我們把IndexMid賦給了leftIndex,也就是認為此時最小的數字位於中間數字的後面。是不是一定一樣?
我們再來看一個例子。陣列{1,0,1,1,1}和陣列{1,1,1,0,1}都可以堪稱遞增排序陣列{0,1,1,1,1}的旋轉,圖2分別畫出它們由最小數字分隔開的兩個子陣列。
這兩種情況中,第一個指標和第二個指標指向的數字都是1,並且兩個指標中間的數字也是1,這3個數字相同。在第一種情況中,中間數字(下標為2)位於後面是子陣列;在第二種情況中,中間數字(下標為2)位於前面的子陣列中。因此,當兩個指標指向的數字及它們中間的數字三者相同的時候,我們無法判斷中間的數字是位於前面的子陣列中還是後面的子陣列中國,也無法移動兩個指標來縮小查詢的範圍。此時,我們不得不採用順序查詢的方法。
在把問題分析清楚後形成清晰的思路之後,我們就可以把前面的程式碼修改為:
Java程式碼實現:
/**
* 把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為旋轉。
* 輸入一個遞增的排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。
* 例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小元素為1.
*/
package swordForOffer;
/**
* @author JInShuangQi
*
* 2015年7月28日
*/
public class E08MinNumberInRotatedArray {
public int minInReversingList(int[] arr){
if(arr==null){
return -1;
}
int leftIndex = 0;
int rightIndex = arr.length -1;
int midIndex = leftIndex;
while(arr[leftIndex]>= arr[rightIndex]){
if(rightIndex - leftIndex <= 1){
midIndex = rightIndex;
break;
}
midIndex = (leftIndex+rightIndex)/2;
if(arr[leftIndex]== arr[rightIndex] && arr[midIndex]== arr[leftIndex]){
return MinInOrder(arr,leftIndex,rightIndex);
}
if(arr[midIndex] >= arr[leftIndex]){
leftIndex = midIndex;
}else if(arr[midIndex] < arr[rightIndex]){
rightIndex = midIndex;
}
}
return arr[midIndex];
}
public int MinInOrder(int[] arr,int leftIndex,int rightIndex){
int result = 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){
E08MinNumberInRotatedArray test = new E08MinNumberInRotatedArray();
int[] arr={3,4,5,1,2}; //{2,2,2,2,2,0,1,2,2};
System.out.println(test.minInReversingList(arr));
}
}