1. 程式人生 > >二叉堆 及 大根堆的python實現

二叉堆 及 大根堆的python實現

cascade 四舍五入 eap 假設 存在 png ade 插入 下標

Python

二叉堆(binary heap)

二叉堆是一種特殊的堆,二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足堆特性:父節點的鍵值總是保持固定的序關系於任何一個子節點的鍵值,且每個節點的左子樹和右子樹都是一個二叉堆。
當父節點的鍵值總是大於或等於任何一個子節點的鍵值時為最大堆。 當父節點的鍵值總是小於或等於任何一個子節點的鍵值時為最小堆。

二叉堆的存儲

二叉堆一般用數組來表示。如果根節點在數組中的位置是1,第n個位置的子節點分別在2n和 2n+1。因此,第1個位置的子節點在2和3,第2個位置的子節點在4和5。以此類推。這種基於1的數組存儲方式便於尋找父節點和子節點。
如果存儲數組的下標基於0,那麽下標為i的節點的子節點是2i + 1與2i + 2;其父節點的下標是?floor((i ? 1) ∕ 2)?, 函數floor(x), 其功能是“向下取整”,或者說“向下舍入”,即取不大於x的最大整數(與“四舍五入”不同,下取整是直接取按照數軸上最接近要求值的左邊值,即不大於要求值的最大的那個值)。比如floor(1.1),floor(1.9)都是返回1。
如下圖的兩個堆:
技術分享圖片


將這兩個堆保存在以1開始的數組中:
位置: 1 2 3 4 5 6 7 8 9 10 11
左圖: 1 2 3 4 5 6 7 8 9 10 11
右圖: 11 9 10 5 6 7 8 1 2 3 4
對於一個很大的堆,這種存儲是低效的。因為節點的子節點很可能在另外一個內存頁中。B-heap是一種效率更高的存儲方式,把每個子樹放到同一內存頁。
如果用指針鏈表存儲堆,那麽需要能訪問葉節點的方法。可以對二叉樹“穿線”(threading)方式,來依序遍歷這些節點。

基本操作

插入節點

在數組的最末尾插入新節點。然後自下而上調整子節點與父節點(稱作up-heap或bubble-up, percolate-up, sift-up, trickle up, heapify-up, cascade-up操作):比較當前節點與父節點,不滿足堆性質則交換。從而使得當前子樹滿足二叉堆的性質。時間復雜度為O(log n)。

刪除節點

刪除根節點用於堆排序。
對於最大堆,刪除根節點就是刪除最大值;對於最小堆,是刪除最小值。然後,把堆存儲的最後那個節點移到填在根節點處。再從上而下調整父節點與它的子節點:對於最大堆,父節點如果小於具有最大值的子節點,則交換二者。這一操作稱作down-heap或bubble-down, percolate-down, sift-down, trickle down, heapify-down, cascade-down,extract-min/max等。直至當前節點與它的子節點滿足堆性質為止。

構造二叉堆

一個直觀辦法是從單節點的二叉堆開始,每次插入一個節點。其時間復雜度為 {\displaystyle O(n\log n)} O(n\log n)。
最優算法是從一個節點元素任意放置的二叉樹開始,自底向上對每一個子樹執行刪除根節點時的Max-Heapify算法(這是對最大堆而言)使得當前子樹成為一個二叉堆。具體而言,假設高度為h的子樹均已完成二叉堆化,那麽對於高度為h+1的子樹,把其根節點沿著最大子節點的分枝做調整,最多需要h步完成二叉堆化。可以證明,這個算法的時間復雜度為O(n)。

代碼

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""


@author: gsharp
"""

class BinaryHeap:
    def __init__(self, n):
        self.heap = [0] * n
        self.size = 0

    def remove_min(self):
        removed = self.heap[0]
        self.size -= 1
        self.heap[0] = self.heap[self.size]
        self._down(0)
        return removed

    def add(self, value):
        self.heap[self.size] = value
        self._up(self.size)
        self.size += 1

    def _up(self, pos):
        while pos > 0:
            parent = (pos - 1) // 2
            if self.heap[pos] >= self.heap[parent]:
                break
            self.heap[pos], self.heap[parent] = self.heap[parent], self.heap[pos]
            pos = parent

    def _down(self, pos):
        while True:
            child = 2 * pos + 1
            if child >= self.size:
                break
            if child + 1 < self.size and self.heap[child + 1] < self.heap[child]:
                child += 1
            if self.heap[pos] <= self.heap[child]:
                break
            self.heap[pos], self.heap[child] = self.heap[child], self.heap[pos]
            pos = child


def test():
    h = BinaryHeap(10)
    h.add(5)
    h.add(7)
    h.add(9)
    h.add(4)
    h.add(3)
    print(h.heap)
    h.add(1)
    print(h.heap)
    h.add(2)
    print(h.heap)
    print(h.remove_min())
    print(h.remove_min())
    print(h.remove_min())


test()

二叉堆 及 大根堆的python實現