題目描述
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。
解法1
對於非減數組來說,數組右邊的元素一定大於等於數組左邊的元素。當對非減數組進行旋轉後(把數組最開始的元素搬到末尾),則在遍歷過程中可能會出現右邊的元素反而小於左邊的元素,當第一次出現這種情況時,一定是原非減數組的開頭,即整個數組的最小元素。
實現代碼
public int minNumberInRotateArray(int[] rotateArray)
{
if (rotateArray.Length <= 0)
{
return 0;
}
for (int i = 1; i < rotateArray.Length; i++)
{
if (rotateArray[i - 1] > rotateArray[i])
{
return rotateArray[i];
}
}
return rotateArray[0];
}
二分查找
解法1是順序遍歷數組找到最小值,這樣的時間複雜度是O(n),那麼有沒有什麼辦法進行優化呢?
本題要查找的是非減排序數組的旋轉數組的最小值。其實是有一定順序的,對於有序數組的查找,我們自然想到了二分查找。
先介紹一下二分查找的基本思想:
首先,假設數組中元素是按升序排列,將數組中間位置元素與要查找的元素比較,如果兩者相等,則查找成功;否則利用中間位置索引將數組分成左、右兩個子數組,如果中間位置的元素大於要查找的元素,則進一步查找左子數組,否則進一步查找右子數組。重複以上過程,直到找到滿足條件的元素,使查找成功,或直到子數組不存在爲止,此時查找不成功。
二分查找的時間複雜度是O(log2n)
解法2
以數組arr = {3,4,5,1,2}爲例,可以分成兩個有序非減數組來看待,如下圖所示
顯然,數組的最小值就在兩個非減數組的交界處,同時由於arr是一個非減數組旋轉得到的,所以左邊數組的最小值一定大於等於右邊數組的最小值。利用二分查找,使左邊的指針指向索引0即3,右邊的索引指向索引4即2,求得mid = low + (high - low)/2 = 2
,比較arr[mid]和arr[high]的值(這裏說明爲什麼不使用arr[mid]和arr[low]進行比較,因爲按照上面的算法,mid有可能等於low,再比較arr[mid]和arr[low]沒有意義)
- 如果arr[mid] > arr[high],則說明當前的mid,處於左邊的非減數組中,則最小值在mid的右邊,則將low指向mid +
1 - 如果arr[mid] < arr[high],則說明當前的mid,處於右邊的非減數組中,則最小值在mid的左邊,則將high指向mid(這裏說明爲什麼high不指向mid - 1,同樣因爲mid的算法,可能存在mid = low = 0,如果high = mid - 1,則high有可能小於0,爲了避免這種情況的判斷,所以採用high = mid )
如果是求一個遞增數組的旋轉數組的最小值,則上述邏輯已經足夠,但本題是求非減數組的旋轉數組的最小值,也就是說可能存在兩個元素相等的情況。
比如旋轉數組{1,0,1,1,1}和{1,1,1,0,1}都可以看成非減數組{0,1,1,1,1}的旋轉數組
此時對於它們而言arr[mid] = arr[high],這種情況下我們並不知道mid是在最小值的左邊還是右邊,比如這兩個旋轉數組,一個是在mid的左邊,一個反而在mid的右邊。當出現這種情況時我們可以認爲數組的有序性丟失了,不能再繼續使用二分查找,而只能順序遍歷從low到high找到最小值,即high = high - 1或者low = low + 1。
實現代碼
public int minNumberInRotateArray(int[] rotateArray)
{
if (rotateArray.Length <= 0) {
return 0;
}
int low = 0, high = rotateArray.Length - 1;
while(high > low) {
int mid = low + (high - low) / 2;
if (rotateArray[mid] > rotateArray[high]) {
low = mid + 1;
}else if (rotateArray[mid] < rotateArray[high]) {
high = mid;
}else {
high = high - 1;
}
}
return rotateArray[low];
}
一點想法
在想到用二分查找優化本題的時候,其實遇到了問題,就是上面有提到的當中間元素等於高位元素時,不知道應該左移還是右移的問題。一度覺得這道題可能用二分查找解不了,後來看到某個大神的代碼,才恍然大悟,這種情況下退化成順序查找就可以。想不到這種方法的原因還是太執着於二分查找的標準形式。一直是在套用算法,而沒有想到變通,或融合其他算法。