1. 程式人生 > >分治法:BFPTR演算法找第k小

分治法:BFPTR演算法找第k小

BFPTR演算法

來自於Blum、Floyd、Pratt、Rivest、Tarjan這5個人,一起釋出了一篇名為 “Time bounds for selection” 的論文,有興趣可以看一下:https://pan.baidu.com/s/1QEWjZBrjEJ7zTIrI99sFYA。

下面是一種實現方式,這種有一個問題,只是找到中位數了,標號還要搜尋然後此時的陣列是有結構規律的,劃分陣列的時候使用的是快排的劃分,沒有用到這裡insert_sort排序產生的區域性有序的特徵,改進優化方法:在找中位陣列中位數的過程中想辦法記錄下來,哪些資料必定小於中位數的在左上方,那些資料必定大於中位數在右下方, 以及不確定的那一塊。

# -*- coding: utf-8 -*-

# 簡單的插入排序
def insert_sort(arr,low,high):
    # 指標i,代表第幾次插入,第0次,就預設首元素放入有序列表
    # 從第1次開始,拿一個元素放入有序列表,或者理解為拿原數組裡面
    # 第i個元素放入有序陣列,在這裡是原地操作的,要麼使用交換,要麼
    # 先拿出一個元素在空中,數組裡面就流出了一個空位
    for i in range(low+1,high+1):
        # 把第i個元素拿起,暫時放在空中
        temp = arr[i]
        # 指標j是從尾部掃描已排列的有序陣列,注意有序列表是從小到大排列的
# j一直都是指在空閒的位置的 j = i # 遇到比空中的元素大的元素,大的元素後移一位,移到空出來的那個位置 # 此時就又空出來一個位置,更新空閒的位置j,...直到碰到比空中的元素小的元素 # 空中元素的位置就找到了,就是現在j指的空閒位置 while arr[j-1] > temp and j >low: arr[j] = arr[j-1] j -=1 # 把空中元素放入空閒位置 arr[
j] = temp # 這個一個可以準確找到中位數的演算法?顯然不是的這個只是大概找中位數的方法,而且還有一個 # 致命的問題,只是找到中位數了,標號還要搜尋 # 然後此時的陣列是有結構規律的,劃分陣列的時候使用的是快排的劃分,沒有用到這裡面 # insert_sort排序產生的區域性有序的特徵,改進優化方法:在找中位陣列中位數的過程中 # 想辦法記錄下來,哪些資料必定小於中位數的在左上方,那些資料必定大於中位數在右下方, # 以及不確定的那一塊 def findMid(arr,low,high): ############################################################################### # Divide ############################################################################### # 這裡的兩個指標為每個小分組的頭和尾,也可以只用一個指標 left = low right = low+4 # count指的是中位數陣列的指標 count = 0 # 當小陣列的尾指標小於等於右邊界限時,對每個小組排序,得到每個小組的中位數 while right <= high: insert_sort(arr,left,right) # 因為中位數是分散的步長為5,對於我來說不好處理,所以此時處理時把中位都放在陣列的 # 前面,這樣下一輪時就比較好處理了,但是這樣處理,就破壞了陣列有序的規律,讓最後 # 無法利用陣列的有序規律來劃分資料,這裡可以優化 arr[low + count],arr[left +2] = arr[left +2],arr[low + count] # 處理下一組 left +=5 right +=5 # 記得更新中位數陣列的指標 count +=1 # 當跳出循壞時,就需要處理餘數,或者沒有進入迴圈,也要處理餘數 # 把餘數排序,獲取中位數,加入到中位數列表 if left <=high: insert_sort(arr,left,high) arr[low + count],arr[left+(high-left)//2] = arr[left+(high-left)//2],arr[low + count] count +=1 # 以上過程是這個問題劃分子問題的過程,也就是partion的過程,也就是把問題規模n變成 # n//5的過程。 ############################################################################### # Conquer ############################################################################### #獲取到中位數陣列,得到了子問題,下面就考慮治conquer了 # 第一種情況,陣列沒有經過5分組,排序後直接到這裡,直接返回在首位的中位數,這種情況下count=1 # 當進入了5分組沒有進入餘數處理,這時count=1,也應該是返回首元素 # 其他情況count>=0時,這兩個元素還沒有排序,所以要進入下一輪排序一下,變成count=1輸出首元素 # count就是low>high的情況,直接返回空 if count ==0: return if count ==1: return arr[low] else: # 注意count是指中位數元素的數量,對應陣列上的指標需要-1 return findMid(arr,low,low+count-1) #標號需要通過檢索得到 def findIndex(arr,mid): for i in range(len(arr)): if arr[i] == mid: return i def partion_with_index(arr,low,high,index): def partition(arr,low,high): # 這時另外一種考慮方式,而且他是不需要額外空間的,他只使用一個指標來區分小於基準和大於基準的 # pointer_less_than代表這個指標的左邊全部都是小於基準的(包括自己,不包括首元素) # 然後從左往右掃描,遇到小於基準的元素,就把小於基準元素區域的後面緊接著的一個元素和他交換 # 那麼小於基準元素區域就多了一個元素,。。。就這樣小於基準的元素就連在了一起 # 首元素是基準元素,小於基準元素區域塊,大於基準元素區域塊,現在分成了三個部分 # 把首元素和小於基準元素區域塊最後一個元素交換,那三部分就變成,小於的,基準,大於的 # 剛開始小於基準的元素為0,暫且指向首位值 pointer_less_than = low # 然後一次掃描後面所有元素 for i in range(pointer_less_than +1,high+1): # 遇到小於基準的,就把小於基準元素區域的後面緊接著的一個元素和他交換,小於的塊相當於也更新了 if arr[i] < arr[low] : pointer_less_than +=1 arr[pointer_less_than],arr[i]=arr[i],arr[pointer_less_than] # 把首元素和小於基準元素區域塊最後一個元素交換,那三部分就變成,小於的,基準,大於的 arr[low],arr[pointer_less_than] = arr[pointer_less_than],arr[low] return pointer_less_than arr[low],arr[index] = arr[index],arr[low] return partition(arr,low,high) def BFPTR(arr,low,high,k): ######################################################################## # divide ######################################################################## # 首先獲取mid mid = findMid(arr,low,high) # 獲取此時mid的index mid_index = findIndex(arr,mid) # 根據mid來劃分資料,返回的是中間的index(就是排序後最終的位置) index =partion_with_index(arr,low,high,mid_index) ####################################################################### # Conquer ######################################################################## #根據index做出子問題的處理 if index == k: return arr[index] elif index > k: return BFPTR(arr,low,index-1,k) else: return BFPTR(arr,index+1,high,k) arr3 = [7,3,66,33,22,66,9,0,1,11,14,17,9,10,13,99,44,33,77,88,99,101,404,87,44,22,11,99,43,22] print(sorted(arr3)) print(BFPTR(arr3,0,len(arr3)-1,(len(arr3)-1)//2-1)) runfile('D:/share/test/BFPTR.py', wdir='D:/share/test') [0, 1, 3, 7, 9, 9, 10, 11, 11, 13, 14, 17, 22, 22, 22, 33, 33, 43, 44, 44, 66, 66, 77, 87, 88, 99, 99, 99, 101, 404] 22