数据结构:堆python实现与堆排序
一、堆的定義
堆是一種完全二叉樹,有最大堆和最小堆兩種。
- 最大堆: 對于每個非葉子節點 V,V 的值都比它的兩個孩子大,稱為 最大堆特性(heap order property) 最大堆里的根總是存儲最大值,最小的值存儲在葉節點。
- 最小堆:和最大堆相反,每個非葉子節點 V,V 的兩個孩子的值都比它大。
二、python實現
??在我們的堆實現中,我們通過創建一個?完整二叉樹?來保持樹平衡。 一個完整的二叉樹是一個樹,其中每個層都有其所有的節點,除了樹的最底層,從左到右填充。下圖展示了完整二叉樹的示例。
完整二叉樹的另一個有趣的屬性是,我們可以使用單個列表來表示它。 因為樹是完整的,父節點的左子節點(在位置 p 處)是在列表中位置 2p 中找到的節點。 類似地,父節點的右子節點在列表中的位置 2p + 1。為了找到樹中任意節點的父節點,我們可以簡單地使用Python 的整數除法。 假定節點在列表中的位置 n,則父節點在位置 n/2。 Figure 2 展示了一個完整的二叉樹,并給出了樹的列表表示。 請注意父級和子級之間是 2p 和 2p+1 關系。 樹的列表表示以及完整的結構屬性允許我們僅使用幾個簡單的數學運算來高效地遍歷一個完整的二叉樹。 我們將看到,這也是我們的二叉堆的有效實現。?
二叉堆實現的基本操作如下:
- BinaryHeap() 創建一個新的,空的二叉堆。
- insert(k) 向堆添加一個新項。
- findMin() 返回具有最小鍵值的項,并將項留在堆中。
- delMin() 返回具有最小鍵值的項,從堆中刪除該項。
- 如果堆是空的,isEmpty() 返回 true,否則返回 false。
- size() 返回堆中的項數。
- buildHeap(list) 從鍵列表構建一個新的堆。
?我們將開始實現一個二叉堆的構造函數。由于整個二叉堆可以由單個列表表示,所以構造函數將初始化列表和一個?currentSize?屬性來跟蹤堆的當前大小。? 你會注意到,一個空的二叉堆有一個單一的零作為?heapList?的第一個元素,這個零只是放那里,用于以后簡單的整數除法。
class BinHeap:def __init__(self):self.heapList = [0]self.currentSize = 0?我們將實現的下一個方法是?insert?。 將項添加到列表中最簡單,最有效的方法是將項附加到列表的末尾。 它維護完整的樹屬性。但可能違反堆結構屬性。可以編寫一個方法,通過比較新添加的項與其父項,我們可以重新獲得堆結構屬性。 如果新添加的項小于其父項,則我們可以將項與其父項交換。 Figure 2 展示了將新添加的項替換到其在樹中的適當位置所需的操作
def percUp(self,i):while i // 2 > 0:if self.heapList[i] < self.heapList[i // 2]:tmp = self.heapList[i // 2]self.heapList[i // 2] = self.heapList[i]self.heapList[i] = tmpi = i // 2 def insert(self,k):self.heapList.append(k)self.currentSize = self.currentSize + 1self.percUp(self.currentSize)我們現在可以看?delMin?方法。 因為堆屬性要求樹的根是樹中的最小項,所以找到最小項很容易。delMin?的難點在根被刪除后恢復堆結構和堆順序屬性。 我們可以分兩步恢復我們的堆。(輸出堆頂元素之后,調整剩余元素稱為一個新堆)首先,我們將通過獲取列表中的最后一個項并將其移動到根位置來恢復根項,保持我們的堆結構屬性。 但是,我們可能已經破壞了我們的二叉堆的堆順序屬性。 第二,我們通過將新的根節點沿著樹向下推到其正確位置來恢復堆順序屬性。 Figure 3展示了將新的根節點移動到堆中的正確位置所需的交換序列。?
def percDown(self,i):while (i * 2) <= self.currentSize:mc = self.minChild(i)if self.heapList[i] > self.heapList[mc]:tmp = self.heapList[i]self.heapList[i] = self.heapList[mc]self.heapList[mc] = tmpi = mcdef minChild(self,i):if i * 2 + 1 > self.currentSize:return i * 2else:if self.heapList[i*2] < self.heapList[i*2+1]:return i * 2else:return i * 2 + 1?delmin?操作的代碼在 如下所示。注意,有難度的工作由輔助函數處理,在這種情況下是?percDown?
def delMin(self):retval = self.heapList[1]self.heapList[1] = self.heapList[self.currentSize]self.currentSize = self.currentSize - 1self.heapList.pop()self.percDown(1)return retval?為了完成我們對二叉堆的討論,我們將看從一個列表構建整個堆的方法。如果我們從整個列表開始,那么我們可以在?O(n)操作中構建整個堆。
def buildHeap(self,alist):i = len(alist) // 2self.currentSize = len(alist)self.heapList = [0] + alist[:]while (i > 0):self.percDown(i)i = i - 1三、堆排序?
實現堆排序需要解決兩個問題:
第二個問題對應delMin方法,輸出堆頂元素,用堆中最后一個元素代替。自堆頂至葉子調整,這個過程稱為篩選。(percDown和minChild)
從一個無序序列建堆的過程就是一個反復篩選的過程,將此序列看成是一個完全二叉樹,則最后一個非終端結點是第個元素。
堆是一種近似完全二叉樹的數據結構,它可以不是完全二叉樹,比如下面這個大根堆。只不過,我們一般喜歡把它構造成完全二叉樹?,主要是為了方便用數組來存儲和計算(根據雙親結點來算孩子,和根據孩子算雙親都很方便)。
?
?完整二叉堆代碼github地址:
https://github.com/makang101/python-data-structure/tree/master/Heap
參考:
數據結構,嚴蔚敏
總結
以上是生活随笔為你收集整理的数据结构:堆python实现与堆排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习:正负样本数据量不平衡处理方法
- 下一篇: Pearson相关系数