思路: 如序列 3,4,5,1,2 觀察到數組旋轉後可以分爲兩個部分,兩個排序的子數組,而且前面的元素都不小於後面數組的元素。而且最小值元素剛好是這兩個數組的分界線。
在有序數組中可以使用二分查找法實現O(logn)的查找。本題的數組一定程度上也是有序的,因此可以使用二分法的思想來尋找這個最小元素。
如上圖所示,a中:array[mid] >= array[p2]說明mid所指還在左邊序列中,p1 = mid;
b中:array[mid] >= array[p2]不成立,則說明mid所指已經在右邊序列中,p2 = mid;
c中 p2 - p1 == 1 已經到達分界線即上一次的mid位置
特殊情況:有序數組前面0個元素後移,如 1,2,3,4,5 代碼應該要考慮這種情況,因此初始化 mid的時候 將mid初始化爲0,如果不執行循環則直接返回array[mid].
超特殊情況:
1 | 0 | 1 | 1 | 1 |
這種情況下 array[p1] = array[p2] = array[mid] 無法判斷mid所指屬於左右哪個序列,這個時候不得不採取順序查找的方式。 這個情況是本題的第二個關鍵。第一個是二分思想。
牛客網版本代碼:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int p = 0;
int q = rotateArray.size() - 1;
int mid = 0; //初始化爲0,防止首元素即爲最小值
while(rotateArray[p] >= rotateArray[q])
{
//分界點
if(q - p == 1)
{
mid = q;
break;
}
mid = (p + q) / 2;
//元素一樣無法二分查找,順序查找
if(rotateArray[p] == rotateArray[mid] &&
rotateArray[q] == rotateArray[mid])
return minInOrder(rotateArray, p, q);
if(rotateArray[mid] >= rotateArray[q])
p = mid;
else
q = mid;
}
return rotateArray[mid];
}
//順序查找最小元素,當序列如1 0 1 1 1時二分查找失效之時
int minInOrder(vector<int> Array, int low, int high)
{
int min = Array[low];
for(int i = low + 1; i <= high; ++i)
if(Array[i] < min)
min = Array[i];
return min;
}
};
有序數組本身就是旋轉的特例。另外還要考慮數組中有相同數字的特例。如果不能很好地處理這些特例,就很難寫出讓面試官滿意的代碼。