學習了堆排序,使用python實現了一個優先隊列結構,記錄一下實現過程:
用一個python的list來表示堆結構,將list作為參數傳入構造函數中,然后在構造函數中建堆:
class prioQueue: def __init__(self, elist=[]): self._elems = list(elist) if elist: self.buildheap()
堆一般都是一個完全二叉樹,那么根據完全二叉樹的性質,一個節點i的左子節點為i+1,右子節點為i*2+1,以最小堆為例,根節點一定是最小值,優先隊列必須保證每次彈出的值都是最小的。建堆過程是一個遞歸的過程,首先拿出list中第一個元素,一個元素必然是一個堆,接下來從list最后拿2個元素分別作為2個子堆,若要將第一個元素這個堆和最后2個元素所構成的子堆合並成一個堆,那么就要保證堆頂的值最小,先判斷左右2個子堆的大小,然后用第一個元素和較小的比較,若小,則不變,若大,則將元素與子堆那個元素調換位置,這樣就形成了1個3個元素的最小堆,之后再從list中拿出第二個元素,從排序好的堆中一次進行這個步驟,若較小,則跳出循環拿出下一個元素,若較大則交換2個比較的元素,再用這個元素與2個子節點的元素進行比較,直到遍歷了整個二叉樹都沒有更大的元素,那么這個元素就將放在元素的最末尾。這便是一個從堆頂即二叉樹根節點開始向下掃描的過程,先定義這個siftDown方法:
def siftdown(self, e,begin, end): elems, i, j, = self._elems, begin , begin*2+1
e為要排序的元素,變量i 儲存我們開始排序的位置,為了不浪費空間,begin參數來作為排序好及位排好的元素list中的索引,所以一開始是0之后遞增,end參數代表着堆元素的個數,也就是len(list)。
然后我們用elems變量保存數組 i作為排序元素的指針 j為i的左子樹 進行循環。只要j沒有到最后就循環下去。
while j < end:
然后先比較左右子樹,使用較小的子樹來和插入的e做比較,若e小於它那么就找到位置,否則調換位置繼續與換位置后的左右子樹進行比較,i位置即是元素e所在的位置:
if j+1<end and elems[j+1] < elems[j]: j = j + 1 if e < elems[j]: break elems[i] = elems[j] i, j = j , 2*j+1 elems[i] = e
對list的每一個元素都進行這樣的一個向下掃描,就可以完成一個建堆的過程,得到一個最小堆:
def buildheap(self): end = len(self._elems) for i in range(end//2,-1,-1): self.siftdown(self._elems[i],i,end) return self._elems