假設一個排好序的陣列在其某一未知點發生了旋轉(比如​0 1 2 4 5 6 7​ 可能變成​4 5 6 7 0 1 2​)。你需要找到其中最小的元素。

線上評測地址:LintCode 領釦

樣例 1:

輸入:[4, 5, 6, 7, 0, 1, 2]

輸出:0

解釋:

陣列中的最小值為0

樣例 2:

輸入:[2,1]

輸出:1

解釋:

陣列中的最小值為1

解題思路

最直接的是暴力解法,搜尋整個陣列找到最小元素,時間複雜度為O(n)。

我們可以旋轉陣列的特性,用改進後的二分查詢來解決,時間複雜度為O(logn)。

演算法流程

九章演算法 | 微軟面試題:尋找旋轉排序陣列中的最小值

使用二分法來尋找最小值,如圖所示可以幫助我們理解演算法過程。

設定雙指標​left​和​right​指向陣列​nums​兩端。

第一次分類討論:比較​nums[left]​和​nums[right]​

如果​nums[left] < nums[right]​,說明陣列沒有旋轉過,仍然是升序排列。我們直接​return nums[left]​。

反之,說明陣列非單調,進入到第二次分類討論。

第二次分類討論:比較​nums[left]​和​nums[mid]​,其中​mid​是二分中點。

如果​nums[left] > nums[mid]​,可以證明此時陣列右半邊是升序的,那我們就不用考慮右半邊了。最小值一定在​[left, mid]​間,令​right = mid​。

如果​nums[left] <= nums[mid]​,可以證明此時陣列左半邊是升序的,於是我們不需要考慮左半邊。最小值一定在​(mid, right]​間,令​left = mid + 1​。

直到​left == right​時,此時指向的就是最小值,​return nums[left]​。

等效方法

上述演算法中,第二次分類討論我們比較的是​nums[left]​和​nums[mid]​的大小關係,其實比較​nums[right]​和​nums[mid]​的大小也是一樣的。

​nums[left] > nums[mid]​,等效於​nums[mid] < nums[right]​

​nums[left] <= nums[mid]​,等效於​nums[mid] > nums[right]​

有興趣可以證明一下。程式碼如何實現,看個人的preference啦。

複雜度分析

時間複雜度: O(log(n)),n為​nums​的長度。同二分查詢的時間複雜度。

空間複雜度: $O(1) $。沒有使用額外空間。

public

class

Solution

{

/**

* @param nums: a rotated sorted array

* @return: the minimum number in the array

*/

public

int

findMin

int

[]

nums

{

int

left

=

0

right

=

nums

length

-

1

// 二分法

while

left

<

right

){

// 最小值在left

if

nums

left

<

nums

right

]){

return

nums

left

];

}

int

mid

=

left

+

right

-

left

/

2

// 最小值在[left, mid]

if

nums

left

>

nums

mid

]){

right

=

mid

}

// 最小值在(mid, right]

else

{

left

=

mid

+

1

}

}

return

nums

left

];

}

}

更多題解參考:九章演算法