算法10:從一個非遞減數組的旋轉數組中找其最小元素值

#算法:求取一個旋轉數組的最小元素
#旋轉數組定義:一個數組中若干元素移到數組的末尾,稱之爲數組的旋轉
#例:array = [1,2,3,4];數組的一個旋轉爲:array = [3,4,1,2]
#移動若干元素,若干包括1個,2個,3個...
#問題:若給定一個非遞減數組的旋轉,如何找到該數組中的最小元素?
#不知道什麼情況下,會出現這麼個問題!
#分析:該旋轉數組原來是有序,且非遞減,則其旋轉後有特點:
#其被移動的若干位構成遞增數列
#剩餘的數列構成另一個遞增數列,且這個數列的所有大於等於前一個數列
#因此該旋轉數組特點:先遞增後減到最小值,然後停止(只移動了一位)或者遞增(移動了1位以上)
#例子:原數組 = [1,2,3,4,5]
#旋轉數組1 = [2,3,4,5,1](移動了一位,先增加後減到最小值,停止)
#旋轉數組2 = [3,4,5,1,2](移動了兩位[多於一位],先增加後減小到最小值,再遞增)
#因此利用"二分法"的思想,我們可以通過中點劃分構成的左,右兩數列的遞增情況,來確定最小值所在的數列區間範圍
#如果一數列一直遞增,則最小數值便在另外一數列中;反之,若該數列有遞減情況,
#則說明最小值在此數列範圍之內。
#那麼如何判斷數列的遞增與否呢?
#由旋轉矩陣的特點可知:若爲遞增,其首尾兩個元素值大小保持遞增順序不變;反之,其數列首尾兩個元素大小值遞減。
#因此,通過劃分的兩個數列的首尾元素值的大小關係便可以確定最小元素所在區間範圍。

#1.在數組中元素沒有冗餘情況時,且有效率要求時;
def MinNumInRotateArray(arr):
    #判斷數組元素是否爲空,空的話返回值爲0
    if len(arr) == 0:
        return 0
    #設定數組索引起點和終點
    l = 0
    h = len(arr) - 1
    #循環結束條件爲區間範圍合理:下限小於上限
    while l<h:
        #計算區間中心點
        m = int(l+(h-l)/2)
        #如果中心點小於數列右端點,說明從中心點到右端點的數組元素構成的數列是遞增數列
        #旋轉數組的最小元素不在此數列中,需將右端點進行更新,設置其爲中心點
        #反之,如果中心點處值大於右端點處值,說明該數列包含被"旋轉"的元素
        #即該數列中含有旋轉數組的最小元素,將區間下限設置爲該中心點的上一位
        #(因爲求最小值,既然中心點大於右端點,則其不可能是最小值了,有理由將其排除出去)
        #即,在區間是遞增情況下時候,更新右端點爲中心點值(中心點處是小於區間右端點值的小值點,不能排除);
        #在區間不是遞增的情況時,更新左端點爲中心點的下一個點值(中心點處是大於區間右端點值的大值點,理應排除)
        if arr[m] <= arr[h]:
            h = m
        else:
            l = m + 1
    return arr[l]


#2.在數組中有冗餘元素時,可能會遇到的情況:中心點,左端點,右端點三處值相同,無法確定
#旋轉數組最小元素所在區間時,需要用到順序查找。
def MinNumInRotateArrayWithSearch(arr):
    if len(arr) == 0:
        return 0
    l = 0
    h = len(arr) - 1
    while l<h:
        mid = int(l + (h-l)/2)
        if arr[l] == arr[h] and arr[h] == arr[mid]:
            SpecialSequentialSearch(arr,l,h)
        elif arr[mid] <= arr[h]:
            h = mid
        else:
            l = mid + 1
    return arr[l]


#一般順序查找函數
def SequentialSearch(arr,l,h):
    minValue = arr[l]
    for i in range(l+1,h+1):
        if arr[i]<minValue:
            minValue = arr[i]
    return minValue


#特殊查找函數
def SpecialSequentialSearch(arr,l,h):
    #因爲旋轉數組的特殊性
    #一旦某個數組元素比其後面的元素大,便知道該處點是旋轉點
    #其後跟隨的是另一個被移動的遞增數列,因此在循環中找到減小點
    #即找到了最小值
    #若循環中沒有找到最小點,即該數組一直不減小,說明該數組已經有序
    #開始點即爲最小點,即返回arr[l]
    for i in range(l,h):
        if arr[i]>arr[i+1]:
            return arr[i+1]
    return arr[l]


#1.處理沒有冗餘元素,且追求高效率的一般情況,用MinNumInRotateArray(arr)函數
a = [3,4,1,2]
print(MinNumInRotateArray(a))
#2.處理可能因爲冗餘元素導致無法確定最小值所在區間的情況,用MinNumInRotateArrayWithSearch(arr)函數
print(MinNumInRotateArrayWithSearch(a))

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