[LeetCode](面試題11)旋轉數組的最小數字

題目

面試題11. 旋轉數組的最小數字
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2][1,2,3,4,5] 的一個旋轉,該數組的最小值爲1。

示例 1:

輸入:[3,4,5,1,2]
輸出:1

示例 2:

輸入:[2,2,2,0,1]
輸出:0

解題思路

旋轉之後的數組可以劃分爲兩個子數組,第一個子數組中的元素都大於等於第二個子數組中的元素。
本題採用二分法,將mid元素與right元素比較,算法步驟如下:
1)如果mid元素小於right元素,說明mid右邊的數組是遞增數組,那麼最小值必然在mid左邊,right = mid;
2)如果mid元素大於right元素,說明最小值在mid與right之間,left = mid + 1;
3)如果相等,說明mid與right區間的元素相等重複,right–;

複雜度分析:
時間複雜度:平均時間複雜度爲 O(logN),其中 N 爲數組長度。但是在最壞情況下,也就是數組中包含相同元素時(nums[mid]==nums[right]),需要逐個遍歷元素,複雜度爲 O(N)。
空間複雜度:O(1)。

一些值得注意的問題:
1)爲什麼不用左邊位置 left 和中間位置 mid 的值進行比較?
舉例:[3, 4, 5, 1, 2] 與 [1, 2, 3, 4, 5] ,此時,中間位置的值都比左邊大,但最小值一個在後面,一個在前面,因此這種做法不能有效地解決問題。
2)爲什麼要用右邊位置 right 和中間位置 mid 的值進行比較?
舉例:[1, 2, 3, 4, 5]、[3, 4, 5, 1, 2]、[2, 3, 4, 5 ,1],用右邊位置和中間位置的元素比較,可以進一步縮小搜索的範圍。
3)當遇到 nums[mid] == nums[right] 的時候,不能確定最小元素在中軸元素的左邊還是右邊。這種情況下,爲了縮小查找範圍,安全的方法是將右邊界指針減一(right = right - 1),這樣可以有效地避免死循環,同時可以保證永遠不會跳過最小元素。
4)數組中元素重複會影響算法的時間複雜度嗎?
可以把問題 153.尋找旋轉排序數組中的最小值 看成這個問題的一個特例。這道題的所有解法也都適用於 153.尋找旋轉排序數組中的最小值 ,只是永遠不會進入 nums[mid] == nums[right] 的分支。也正是因爲可能包含重複數字,在 nums[mid] == nums[right] 分支下才會導致最差時間複雜度爲 O(N)。

代碼

class Solution {
    public int minArray(int[] numbers) {
        if(numbers.length == 0){
            return 0;
        }
        int left = 0;
        int right = numbers.length - 1;
        while(left<right){
            int mid = left + (right - left)/2;
            if(numbers[mid] < numbers[right]){
                // 後半部分遞增有序,最小值在前半部分,含mid
                right = mid;
            }else if(numbers[mid] > numbers[right]){
                // 前半部分遞增有序,最小值在後半部分,不含mid
                left = mid + 1;
            }else{
                // 如果相等,right--
                right--;
            }
        }
        return numbers[left];
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章