算法題解:旋轉數組的最小數字

題目描述

把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲數組的旋轉。輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。java

解題思路

將旋轉數組對半分能夠獲得一個包含最小元素的新旋轉數組,以及一個非遞減排序的數組。新的旋轉數組的數組元素是原數組的一半,從而將問題規模減小了一半,這種折半性質的算法的時間複雜度爲 O(logN)(爲了方便,這裏將 log2N 寫爲 logN)。算法

此時問題的關鍵在於肯定對半分獲得的兩個數組哪個是旋轉數組,哪個是非遞減數組。咱們很容易知道非遞減數組的第一個元素必定小於等於最後一個元素。數組

經過修改二分查找算法進行求解:code

  • 當 nums[mid] <= nums[high] 時,表示 [mid, high] 區間內的數組是非遞減數組,[low, mid] 區間內的數組是旋轉數組,此時令 high = mid;
  • 不然 [mid + 1, high] 區間內的數組是旋轉數組,令 low = mid + 1。
public int minNumberInRotateArray(int[] arr) {
    if (arr.length == 0) {
        return 0;
    }
    int low = 0;
    int high = arr.length - 1;
    while (low < high) {
        int mid = low + (high - low) / 2;
        // 注意此處比較的是後半段
        if (arr[mid] <= arr[high]) { 
            high = mid;
        } else {
            low = mid + 1;
        }
    }
    return arr[low];
}

若是數組元素容許重複,會出現一個特殊的狀況:nums[low] == nums[mid] == nums[high],此時沒法肯定解在哪一個區間,須要切換到順序查找。例如對於數組 {1, 1, 1, 0, 1},low、mid 和 high 指向的數都爲 1,此時沒法知道最小數字 0 在哪一個區間。blog

public int minNumberInRotateArray(int[] arr) {
    if (arr.length == 0) {
        return 0;
    }
    int low = 0;
    int high = arr.length - 1;
    while (low < high) {
        int mid = low + (high - low) / 2;
        if (arr[low] == arr[mid] && arr[mid] == arr[high]) {
            return minNumber(arr, low, high);
        } else if (arr[mid] <= arr[high]) {
            high = mid;
        } else {
            low = mid + 1;
        }
    }
     return arr[low];
}

private int minNumber(int[] arr, int low, int high) {
    for (int i = low; i < high; i++) {
        if (arr[i] > arr[i + 1]) {
            return arr[i + 1];
        }
    }
    return arr[low];
}

參考

CyC2018排序

相關文章
相關標籤/搜索