python數據結構之堆(heap)


本篇學習內容為堆的性質、python實現插入與刪除操作、堆復雜度表、python內置方法生成堆。

區分堆(heap)與棧(stack):堆與二叉樹有關,像一堆金字塔型泥沙;而棧像一個直立垃圾桶,一列下來。

堆(heap)

又被為優先隊列(priority queue)。盡管名為優先隊列,但堆並不是隊列。回憶一下,在隊列中,我們可以進行的限定操作是dequeue和enqueue。

dequeue是按照進入隊列的先后順序來取出元素。而在堆中,我們不是按照元素進入隊列的先后順序取出元素的,而是按照元素的優先級取出元素。

性質

堆的實現通過構造二叉堆(binary heap),實為二叉樹的一種;由於其應用的普遍性,當不加限定時,均指該數據結構的這種實現。這種數據結構具有以下性質。

  • 任意節點小於(或大於)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
  • 堆總是一棵完全樹。即除了最底層,其他層的節點都被元素填滿,且最底層盡可能地從左到右填入。

實現

  • 堆的主要操作是插入和刪除最小元素(元素值本身為優先級鍵值,小元素享有高優先級)
  • 在插入或者刪除操作之后,我們必須保持該實現應有的性質: 1. 完全二叉樹 2. 每個節點值都小於或等於它的子節點

上浮(Promotion)

情境: 子節點的鍵值變為比父節點的鍵值大;如下面添加字節點

消除這種違反項: 

  • 交換子節點的鍵和父節點的鍵 
  • 重復這個過程直到堆的順序恢復正常

堆的添加:

def _upheap(self, j):#往上交換
        parent = self._parent(j) 
        if j > 0 and self._data[j] < self._data[parent]: 
            self._swap(j, parent) 
            self._upheap(parent) 

下沉(Demotion) 

情境:父節點的鍵值變得比子節點(一個或者2個) 的鍵值還小 ,如下面刪除了根節點后拿了個小子節點補充上來的情況

消除這種違反項:

  • 把父節點的鍵值和比它大的子節點的鍵值做交換
  • 重復這個操作直到堆的順序恢復正常

刪除最大值

def _downheap(self, j):#往下交換,遞歸比較三個值
        if self._has_left(j):
            left = self._left(j)
            small_child = left
            if self._has_right(j):
                right = self._right(j) 
                if self._data[right] < self._data[left]:
                    small_child = right 
            if self._data[small_child] < self._data[j]:
                self._swap(j, small_child) 
                self._downheap(small_child)

復雜度分析

Python構建堆的代碼:

#該heap為min_heap,即根節點為最小值
class PriorityQueueBase:
    #抽象基類為堆

    class Item: 
        #輕量級組合來存儲堆項目
        __slots__ = '_key' , '_value'

        def __init__ (self, k, v):
            self._key = k
            self._value = v

        def __lt__ (self, other):     #比較大小                                   
            return self._key < other._key

        def is_empty(self):
            return len(self) == 0   

        def __str__(self):
            return str(self._key)
        

class HeapPriorityQueue(PriorityQueueBase):

    def __init__ (self):
        self._data = [ ]         

    def __len__ (self):
        return len(self._data)
    
    def is_empty(self):
        return len(self) == 0  

    def add(self, key, value):   #在后面加上然后加上
        self._data.append(self.Item(key, value)) 
        self._upheap(len(self._data) - 1)
        
    def min(self): 
        if self.is_empty():
            raise ValueError( "Priority queue is empty." )
        item = self._data[0]
        return (item._key, item._value)
    
    def remove_min(self):
        if self.is_empty():
            raise ValueError( "Priority queue is empty." )
        self._swap(0, len(self._data) - 1)
        item = self._data.pop( )
        self._downheap(0)
        return (item._key, item._value)

    def _parent(self, j): 
        return (j - 1) // 2
    
    def _left(self, j):
        return 2 * j + 1
    
    def _right(self, j):
        return 2 * j + 2

    def _has_left(self, j):
        return self._left(j) < len(self._data)
    
    def _has_right(self, j):
        return self._right(j) < len(self._data)      
    
    def _swap(self, i, j):
        self._data[i], self._data[j] = self._data[j], self._data[i]
        
    def _upheap(self, j):#往上交換
        parent = self._parent(j) 
        if j > 0 and self._data[j] < self._data[parent]: 
            self._swap(j, parent) 
            self._upheap(parent) 
    
    def _downheap(self, j):#往下交換,遞歸比較三個值
        if self._has_left(j):
            left = self._left(j)
            small_child = left
            if self._has_right(j):
                right = self._right(j) 
                if self._data[right] < self._data[left]:
                    small_child = right 
            if self._data[small_child] < self._data[j]:
                self._swap(j, small_child) 
                self._downheap(small_child)  

heap = HeapPriorityQueue()
heap.add(4, "D")
heap.add(3, "C")
heap.add(1, "A")
heap.add(5, "E")
heap.add(2, "B")
heap.add(7, "G")
heap.add(6, "F")
heap.add(26, "Z")

for item in heap._data:
    print(item)

print("min is: ")
print(heap.min())
print()

print("remove min: ")
print(heap.remove_min())
print("Now min is: ")
print(heap.min())
print()

print("remove min: ")
print(heap.remove_min())
print("Now min is: ")
print(heap.min())
print()

heap.add(1, "A")
print("Now min is: ")
print(heap.min())
print()

#輸出結果
1
2
3
5
4
7
6
26
min is: 
(1, 'A')

remove min: 
(1, 'A')
Now min is: 
(2, 'B')

remove min: 
(2, 'B')
Now min is: 
(3, 'C')

Now min is: 
(1, 'A')

python內置方法創建堆有兩種方式,heappush()和heapify()

'''
heaqp模塊提供了堆隊列算法的實現,也稱為優先級隊列算法。
要創建堆,請使用初始化為[]的列表,或者可以通過函數heapify()將填充列表轉換為堆。
提供以下功能:
heapq.heappush(堆,項目)
將值項推入堆中,保持堆不變。
heapq.heapify(x)
在線性時間內將列表x轉換為堆。
heapq.heappop(堆)
彈出並返回堆中的最小項,保持堆不變。如果堆是空的,則引發IndexError。
'''
import heapq 

#1 heappush生成堆+ heappop把堆從小到大pop出來 
heap = []
data = [1,3,5,7,9,2,4,6,8,0]
for i in data:
    heapq.heappush(heap,i)
print(heap)

lis = []
while heap:
    lis.append(heapq.heappop(heap))
print(lis)

#2 heapify生成堆+ heappop把堆從小到大pop出來 
data2 = [1,5,3,2,9,5]
heapq.heapify(data2)
print(data2)

lis2 = []
while data2:
    lis2.append(heapq.heappop(data2))
print(lis2)

#輸出結果
[0, 1, 2, 6, 3, 5, 4, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 5, 9, 5]
[1, 2, 3, 5, 5, 9]

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM