數組旋轉問題
題目描述
把一個數組最開始的若干個元素搬到數組末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出數組最小的元素。例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
解題思路
採用二分法的思路,設定兩個指針分別指向數組的第一個元素和最後一個元素。(第一個元素應該是大於或者等於最後一個元素的,存在特例)找到數組中間的元素(mid=(head+tail)/2),如果中間的元素位於前面的遞增子數組,那麼它應該大於或者等於第一個指針指向的元素。此時數組中最小的元素應該位於該中間元素的後面。我們可以把第一個指針指向該中間元素,這樣可以縮小尋找的範圍。移動之後的第一個指針仍然位於前面的遞增數組中。
如果中間元素位於後面的遞增子數組,那麼它應該小於或者等於第二個指針指向的元素。此時該數組中最小的元素應該位於該中間元素的前面。我們可以把第二個指針指向該中間元素,這樣也可以縮小尋找的範圍。移動之後的第二個指針仍然位於後面的遞增子數組之中。
因此,第一個指針總是指向前面遞增的數字元素,而第二個指針總是指向後面遞增的數組的元素。最終第一個指針將指向前面子數組的最後一個元素,而第二個指針將指向後面子數組的第一個元素。也就是他們最終會指向兩個相鄰的元素,而第二個指針指向的剛好是最小的元素。這就是循環結束的條件。
注意兩種特殊情況:
{1,0,1,1,1}和數組{1,1,1,0,1}都可以看成是遞增數組{0,1,1,1,1}的旋轉。在這兩個數組中,第一個數字、最後一個數字和中間數字都是1,我們無法確定中間數字1屬於第一個遞增子數組還是屬於第二個遞增子數組。(實際上,第一種情況下,中間數字位於後面的子數組;第二種情況中,中間數字位於前面的子數組中)
public class Code008 {
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
String[] inLine=sc.nextLine().split(" ");
int len=inLine.length;
int[] arr=new int[len];
for(int i=0;i<len;i++){
arr[i]=Integer.parseInt(inLine[i]);
}
int result=solution(arr,len);
System.out.println(result);
}
private static int solution(int[] arr,int len){
if(arr == null || len<0){
return -1;
}
int index1=0;
int index2=len-1;
int indexMid=index1;
while (arr[index1]>=arr[index2]){
if(index2-index1==1){
indexMid=index2;
break;
}
//兩指針正中間的數字對應的下標
indexMid=(index1+index2)/2;
//如果下標爲index1和index2和indexMid的三個數字相等,那麼只能順序查找了
if(arr[index1]==arr[index2] && arr[indexMid]==arr[index1]){
return minIndexOrder(arr,index1,index2);
}
//如果左指針指向的數字小於中間的數字,那麼目標數字位於中間數與尾指針之間
if(arr[indexMid]>=arr[index1]){
index1=indexMid;
}
//如果尾指針指向數字大於中間數字,那麼目標數字位於左指針與中間數字之間
if(arr[indexMid]<=arr[index2]){
index2=indexMid;
}
}
return arr[indexMid];
}
private static int minIndexOrder(int[] arr, int index1, int index2) {
int result=arr[index1];
for(int i=index1+1;i<=index2;i++){
if(result>arr[i]){
result=arr[i];
}
}
return result;
}
}