把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。 NOTE:給出的所有元素都大于0,若數組大小為0,請返回0。
一個有序數組旋轉后,分成了兩個有序的子數組,這樣一個大數組相當于一定程度上是有序的。因此可以嘗試使用二分法。mid = low + (high - low)/2,始終將mid和high處的值作比較。分三種情況。
- array[mid] > array[high] 此時mid肯定還處于左側數組之中,要找的最小值在右側數組中,最小值肯定在mid的右側,所以可以直接把low移動到mid的下一個位置,即low=mid+1。
- array[mid] < array[high] 此時mid肯定處于右側數組之中,要找的最小值可能為mid的值或者mid右邊的值,所以high只能縮小到mid,不能縮小到mid-1。
- array[mid] == array[high],此時無法分辨mid處于左半數組還是右半數組。比如{1, 0, 1, 1, 1}和{1, 1, 1, 0, 1}都是數組{0, 1, 1, 1, 1}的旋轉數組。此時mid處和high處的值一樣,若根據low縮小范圍,對于{1, 0, 1, 1, 1}最小值將被跳過;如果根據high縮小范圍,對于{1, 1, 1, 0, 1}最小值也會被跳過。所以此時只能放棄二分查找,既然mid處和high處的值相同,那么就讓high--,讓mid和high前面的值作比較。如果mid和high都是最小值,就算放棄了high也能在mid處找到最小值。
只要low<high(不含等于),就一直重復以上比較過程,一直比到low與high指向同一個元素,到low==high的時候退出,其實low == high時候還進入循環,也沒有錯,此時只會造成high--,而我們返回的是array[low],值不會影響,但是何必進行這次無意義的比較呢。
PS:為什么要把mid和high作比較,而不能讓mid和low作比較,試想如果最后范圍縮小到剩下{1, 2}此時array[low] == array[mid],如果low++就跳過最小元素了,此種情況可以寫一個min(int low, int high)
方法,直接返回[low, high]范圍內的最小值。比起用high來和mid比較,麻煩了不少。
public int minNumberInRotateArray(int [] array) {
if (array==null || array.length == 0) {
return 0;
}
int low = 0;
int high = array.length - 1;
while (low < high) {
int mid = low + (high - low) / 2;
// 此時mid一定在左半數組中,最小值在mid的右邊
if (array[mid] > array[high]) {
low = mid + 1;
// 此時mid一定在右半數組中,最小值在mid的左邊或就是mid本身
} else if (array[mid] < array[high]) {
high = mid;
// 暫時放棄二分查找,和前一個字符繼續比較
} else {
high--;
}
}
// low == high時推出循環并返回
return array[low];
}