把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的全部元素都大於0,若數組大小爲0,請返回0。web
對於非減數組來講,數組右邊的元素必定大於等於數組左邊的元素。當對非減數組進行旋轉後(把數組最開始的元素搬到末尾),則在遍歷過程當中可能會出現右邊的元素反而小於左邊的元素,當第一次出現這種狀況時,必定是原非減數組的開頭,即整個數組的最小元素。算法
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)數組
以數組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]沒有意義)svg
若是是求一個遞增數組的旋轉數組的最小值,則上述邏輯已經足夠,但本題是求非減數組的旋轉數組的最小值,也就是說可能存在兩個元素相等的狀況。
好比旋轉數組{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]; }
在想到用二分查找優化本題的時候,其實遇到了問題,就是上面有提到的當中間元素等於高位元素時,不知道應該左移仍是右移的問題。一度以爲這道題可能用二分查找解不了,後來看到某個大神的代碼,才恍然大悟,這種狀況下退化成順序查找就能夠。想不到這種方法的緣由仍是太執着於二分查找的標準形式。一直是在套用算法,而沒有想到變通,或融合其餘算法。spa