1. 程式人生 > 實用技巧 >每日一題 - 11. 旋轉陣列的最小數字

每日一題 - 11. 旋轉陣列的最小數字

題目資訊

  • 時間: 2019-07-23

  • 題目連結:Leetcode

  • tag: 二分查詢

  • 難易程度:簡單

  • 題目描述:

    把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如,陣列 [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

解題思路

本題難點

排序陣列的旋轉,找到旋轉點,效能最佳。

具體思路

尋找旋轉陣列的最小元素即為尋找 右排序陣列 的首個元素 numbers[x]

,稱 x為 旋轉點

排序陣列的查詢問題首先考慮使用 二分法 解決,其可將遍歷法的 線性級別 時間複雜度降低至 對數級別

  • 迴圈二分:
    1. 當 numbers[m] > numbers[j]時: m 一定在 左排序陣列 中,即旋轉點 x 一定在 [m+1,j] 閉區間內,因此執行 i=m+1;
    2. 當 numbers[m] < numbers[j] 時:m 一定在 右排序陣列 中,即旋轉點 x 一定在[i,m] 閉區間內,因此執行 j=m;
    3. 當 numbers[m] == numbers[j] 時: 無法判斷 m 在哪個排序陣列中,即無法判斷旋轉點 x 在 [i,m] 還是 [m+1,j] 區間中。解決方案: 執行 j=j−1 縮小判斷範圍 。

展開分析 numbers[m] == numbers[j] 情況

  • 無法判定 m 在左(右)排序陣列: 設以下兩個旋轉點值為 0 的示例陣列,則當 i=0, j=4 時 m=2 ,兩示例結果不同。

    • 例 [1,0,1,1,1] :旋轉點 x=1 ,因此 m=2 在 右排序陣列 中。

    • 例 [1,1,1,0,1] :旋轉點 x=3 ,因此 m=2 在 左排序陣列 中。

  • j=j−1 操作的正確性證明:只需證明每次執行此操作後,旋轉點 x 仍在 [i,j] 區間內即可。

    • 若 m 在右排序陣列中: numbers[m] == numbers[j] ,因此陣列 [m,j](恆有 m<j)區間內所有元素值相等,執行 j=j−1 只會拋棄一個重複值,因此旋轉點 x 仍在 [i,j] 區間內。

    • 若 m 在左排序陣列中: 由於 左排序陣列 任一元素 >= 右排序陣列 任一元素 ,因此可推出旋轉點元素值 numbers[x] <= numbers[j] == numbers[m],則有:

      1. 若 numbers[x] < numbers[j] : 即 j 左方仍有值更小的元素,執行 j=j−1 後旋轉點 x 仍在 [i,j] 區間內。

      2. numbers[x] == numbers[j] 分為以下兩種情況。

        當 j>xj>x : 易得執行 j=j−1j=j−1 後旋轉點 xx 仍在 [i,j] 區間內。

        當 j=x: 特殊情況,即執行 j=j−1 後旋轉點 x 可能不在 [i,j] 區間內。例如 [1,1,1,2,3,1] ,當 i=0 , m=2 , j=5 時執行 j=j−1 後雖然 丟失了旋轉點索引 x=5 ,但最終返回值仍正確(最終返回的 numbers[0] 等於旋轉點值 numbers[5] ),這是因為:之後的二分迴圈一直在執行 j=m ,而區間 [i,m] 內的元素值一定都等於旋轉點值 numbers[x] ( ∵ 區間內元素值既要滿足 ≥ 也要滿足 ≤ numbers[x]) ,因此 仍可保證正確的返回值 。

提示 是否可以用 numbers[m]numbers[i] 比較做代替?不可以。因為做比較的目的是判斷 m 在哪個排序陣列中。但在 numbers[m] > numbers[i]情況下,無法判斷 m 在哪個排序陣列中。本質是因為 j 初始值肯定在右排序陣列中; i 初始值無法確定在哪個排序陣列中。

程式碼

class Solution {
    public int minArray(int[] numbers) {
        int l = 0;
        int r = numbers.length-1;
        while(l < r){
            int mid = (l + r)/2;
            if(numbers[mid] > numbers[r]){
                l = mid + 1;
            }else if(numbers[mid] < numbers[r]){
                r = mid;
            }else{
                r--;
            }
        }
        return numbers[l];
    }
}

複雜度分析:

  • 時間複雜度 O(logN) : 在特例情況下(例如 [1,1,1,1]),會退化到 O(N)。
  • 空間複雜度 O(1) : i , j , m指標使用常數大小的額外空間。