最大堆(二)
阿新 • • 發佈:2018-11-11
背景知識:
- 堆是一棵完全二叉樹,什麼是完全二叉樹?就是除了最後一層節點之外,其他層的節點個數必須是最大值,並且最後一層的節點必須都集中在左側。
- 堆分為最大堆和最小堆。最大堆就是父節點大於等於子節點,從而導致根節點是最大值。最小堆就是父節點小於等於子節點,從而導致根節點是最小值。本文基於最大堆講解,並且提供python程式碼實現。
1.在第一節中我們詳細講解了什麼是最大堆,以及基於列表這種資料結構的最大堆的python實現方案,聰明的小夥伴可能已經發現在第一節中,我們將原列表資料變成堆的過程是循壞執行了一個叫做Shift Up的方法,那這麼做效率是不是不高呢?答案是肯定,為此我們對原來的堆類進行優化,使用Heapify演算法
2.首先簡單介紹一下Heapify演算法:一個最大堆,從上而下從左到右,依次從1開始標序直到最後一個節點,這時候你可以發現一個規律:從下往上,第一個非葉子節點的序號是節點總個數除以二取整。Heapify演算法就是以這個規律為基礎,對於原列表構建堆的過程不在是迴圈執行Shift Up,而是直接忽略葉子節點,從第一個非葉子節點開始,向上追溯,把每一部分當作是一棵完全二叉樹執行Shift Down來構建最大堆,直至追溯到根節點,那麼整棵完全二叉樹就是一個最大堆了。具體的圖解如下:
3.經過了第二步的分析,相信讀者對於Heapify演算法已經很清楚了,下面提供python的具體實現:
import random # 使用heapify演算法優化(heapify演算法其實就是隻考慮非葉子節點,並向上追溯將每一部分看成一個完全二叉樹並執行shiftDown變成最大堆, # 直至向上追溯到下標為1,那麼整個完全二叉樹就是一個最大堆了。 class maxStack1: def __init__(self, list, n): self.data = [0] for i in range(n): self.data.append(list[i]) self.count = n # heapify演算法 # self.count//2得到的就是第一個非葉子節點的下標,-1表示向上追溯 for key in range(self.count // 2, 0, -1): self.__shiftDown(key) # 首先比較當前節點的兩個子節點的大小,如果大的那個子節點比父元素大就交換位置 def __shiftDown(self, key): # 在完全二叉樹中,只要保證有左節點那麼這個節點就是有孩子的,因為完全二叉樹並不存在有右節點而沒有左節點 while 2 * key <= self.count: # 預設change指向左節點 change = 2 * key # 如果存在右節點並且右節點比左節點大,那麼將change加1,此時change指向了右節點 if change + 1 <= self.count and self.data[change + 1] > self.data[change]: change += 1 # 經過上面兩步,此時change指向的值是key父節點最大的孩子節點 if self.data[change] <= self.data[key]: break self.data[change], self.data[key] = self.data[key], self.data[change] key = change if __name__ == '__main__': n=10 list=[random.randint(0,n) for i in range(n)] print(list) maxStack = maxStack1(list,n) print(maxStack.data)
4.堆排序的時間消耗主要在初始堆的構建和反覆重建堆這兩部分上,第一節中我們使用迴圈執行Shift Up來構建初始堆,時間複雜度是O(NlogM),經過Heapify演算法優化之後,構建初始堆的時間複雜度變為了O(N)。