1. 程式人生 > >旋轉陣列的最小數字(python)

旋轉陣列的最小數字(python)

原始碼

題目描述

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

思路:

劍指Offer中有這道題目的分析。這是一道二分查詢的變形的題目。

旋轉之後的陣列實際上可以劃分成兩個有序的子陣列:前面子陣列的大小都大於後面子陣列中的元素

注意到實際上最小的元素就是兩個子陣列的分界線。本題目給出的陣列一定程度上是排序的,因此我們試著用二分查詢法尋找這個最小的元素。

思路:

(1)我們用兩個指標left,right分別指向陣列的第一個元素和最後一個元素。按照題目的旋轉的規則,第一個元素應該是大於最後一個元素的(沒有重複的元素)。

但是如果不是旋轉,第一個元素肯定小於最後一個元素。

(2)找到陣列的中間元素。

中間元素大於第一個元素,則中間元素位於前面的遞增子陣列,此時最小元素位於中間元素的後面。我們可以讓第一個指標left指向中間元素。

移動之後,第一個指標仍然位於前面的遞增陣列中。

中間元素小於第一個元素,則中間元素位於後面的遞增子陣列,此時最小元素位於中間元素的前面。我們可以讓第二個指標right指向中間元素。

移動之後,第二個指標仍然位於後面的遞增陣列中。

這樣可以縮小尋找的範圍。

(3)按照以上思路,第一個指標left總是指向前面遞增陣列的元素,第二個指標right總是指向後面遞增的陣列元素。

最終第一個指標將指向前面陣列的最後一個元素,第二個指標指向後面陣列中的第一個元素。

也就是說他們將指向兩個相鄰的元素,而第二個指標指向的剛好是最小的元素,這就是迴圈的結束條件。

到目前為止以上思路很耗的解決了沒有重複數字的情況,這一道題目新增上了這一要求,有了重複數字。

因此這一道題目比上一道題目多了些特殊情況:

我們看一組例子:{1,0,1,1,1} 和 {1,1, 1,0,1} 都可以看成是遞增排序陣列{0,1,1,1,1}的旋轉。

這種情況下我們無法繼續用上一道題目的解法,去解決這道題目。因為在這兩個陣列中,第一個數字,最後一個數字,中間數字都是1。

第一種情況下,中間數字位於後面的子陣列,第二種情況,中間數字位於前面的子陣列。

因此當兩個指標指向的數字和中間數字相同的時候,我們無法確定中間數字1是屬於前面的子陣列(綠色表示)還是屬於後面的子陣列(紫色表示)。

也就無法移動指標來縮小查詢的範圍。

class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return 0
        # 建立前後指標
        front, rear = 0, len(rotateArray) - 1
        # 中值數字
        midIndex = 0
        # 確保旋轉
        while rotateArray[front] >= rotateArray[rear]:
            # 當首尾僅差距1時,最後位為最小值
            if rear - front == 1:
                midIndex = rear
                break
            midIndex = (front + rear) // 2
            # 無法確定中間元素是屬於前面還是後面的遞增子陣列
            # 只能順序查詢
            if rotateArray[front] == rotateArray[rear] and rotateArray[front] == rotateArray[midIndex]:
                return self.minOrder(rotateArray, front, rear)
            # 二分查詢,又不同於二分查詢,旋轉之後的陣列實際上可以劃分成兩個有序的子陣列:
            # 前面子陣列的大小都大於後面子陣列中的元素
            # 中間元素大於第一個元素,則中間元素位於前面的遞增子陣列,
            # 此時最小元素位於中間元素的後面。我們可以讓第一個指標front指向中間元素。
            if rotateArray[front] <= rotateArray[midIndex]:
                front = midIndex
            # 中間元素小於第一個元素,則中間元素位於後面的遞增子陣列,
            # 此時最小元素位於中間元素的前面。我們可以讓第二個指標rear指向中間元素。
            elif rotateArray[rear] >= rotateArray[midIndex]:
                rear = midIndex

        return rotateArray[midIndex]

    # 順序查詢
    def minOrder(self, array, front, end):
        result = array[0]
        for i in array[front:end + 1]:
            if i < result:
                result = i
        return result