劍指offer-chapter2-面試題8-旋轉數組的最小數字(java)

題目:

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。

分析&解法:

解法1:

首先可以使用遍歷的方法找出其中最小數字,但是由於該時間複雜度爲O(n),不是最優解法。

解法2:

雖然是旋轉數組,但是基本還是有一定的排序規律的,通過觀察可以發現經過旋轉後遞增區域劃分成了兩半如圖:

3 4 5 1 2
左邊較大值大遞增區域 右邊較小值遞增區域

故通過這一特點我們可以考慮使用二分查找的方法:

初始化 (陰影區域爲最小值所在區域):

3 4 5 1 2
startIndex midIndex endIndex

通過分別和startIndex、endIndex比較大小後,可分爲三種情況:

比startIndex大:
則最小值在midIndex-endStart之間
比endIndex小:
則最小值在startIndex-midIndex之間
三者相等:
舉例:{1,0,1,1,1} 和{1,1,1,0,1}都可以視爲 {0,1,1,1,1}的旋轉
但是他們三者相等,最小值卻分別在左邊和右邊,故無法判斷,轉爲使用遍歷方法。

以剛纔的例子進行第一次劃分:

3 4 5 1 2
startIndex midIndex endIndex

第二次劃分:

3 4 5 1 2
startIndex endIndex

則當兩個index相鄰的時候可以的代minIndex

代碼如下:

package problem8;

/**
 * Created by fengyuwusong on 2018/1/29 15:09.
 * 題目描述
 * 把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。
 * 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。
 * 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
 * NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。
 * 陷阱:
 * 1. 把一個數組最開始的若干個元素搬到數組的末尾 若干個包括0 級排序數組本身
 * 2. {1,0,1,1,1} 和{1,1,1,0,1}都可以視爲 {0,1,1,1,1}的旋轉
 */
public class Main {
    // 解法1:遍歷  時間複雜度O(n) 不考慮
    // 解法2:二分查找 時間複雜度 O(logn)
    public int minNumberInRotateArray(int[] array) {
        if (array.length==0){
            return 0;
        }
        int startIndex,endIndex,midIndex;
        startIndex=0;
        endIndex=array.length-1;
//        預防陷阱1
        midIndex=array[startIndex];

        while (array[startIndex]>=array[endIndex]){
//            跳出循環條件 如果start和end索引相鄰
            if (endIndex-startIndex==1){
                midIndex=endIndex;
                break;
            }
//            找出中間 並移動座標
            midIndex=(startIndex+endIndex)/2;
//            預防陷阱2 如果start end mid值相等 則只能按照順序查找
            if (array[startIndex]==array[endIndex]&&array[startIndex]==array[midIndex]){
                return MinInOrder(array,startIndex,endIndex);
            }
            if (array[midIndex]>=array[startIndex]){
                startIndex=midIndex;
            }else if (array[midIndex]<=array[endIndex]){
                endIndex=midIndex;
            }
        }
        return array[midIndex];
    }

    private int MinInOrder(int[] array, int startIndex, int endIndex) {
        int min=array[startIndex];
        for (int i = startIndex; i <=endIndex; i++) {
//            優化遍歷 觀察可發現一旦後面小於前面第一個則該值最小
            if (array[startIndex]>=array[i]){
                min=array[i];
            }
        }
        return min;
    }

    public static void main(String[] args) {
        Main main=new Main();
        int min=main.minNumberInRotateArray(new int[]{3,4,5,1,2});
        System.out.println(min);
    }
}

陷阱&難點:

1. 把一個數組最開始的若干個元素搬到數組的末尾 若干個包括0 級排序數組本身

2. {1,0,1,1,1} 和{1,1,1,0,1}都可以視爲 {0,1,1,1,1}的旋轉

解決方法:

1. 將min的初始值設爲0。

2. 當startIndex、endIndex、midIndex值相等時使用函數MinInOrder進行遍歷查找,遍歷查找中根據規律做適當優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章