友情鏈接:heapq 堆
heapq
python內置heapq
模塊,通過import heapq
導入。
heapq模塊是用於堆實現優先隊列。我們知道隊列是先進先出(FIFO),
heapq中的優先隊列指的是不論誰先進,最小的先出或者最大的先出。
# 需要注意的是heapq的堆是小根堆。
0
1 2
3 4 5 6
7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# 堆頂位置的元素最小,heapq和python一致,下標索引從0開始。
# heapq提供的主要API, Usage:
heap = [] # creates an empty heap
heappush(heap, item) # pushes a new item on the heap
item = heappop(heap) # pops the smallest item from the heap
item = heap[0] # smallest item on the heap without popping it
heapify(x) # transforms list into a heap, in-place, in linear time
item = heapreplace(heap, item) # pops and returns smallest item, and adds
# new item; the heap size is unchanged
heapq實現列表排序
基本排序流程
# -*- coding: utf-8 -*-
# created by X. Liu on 2020/3/14
import heapq
import random
li = [i for i in range(10)]
random.shuffle(li)
print('排序前', li)
# step1: 建堆(小根堆)
heapq.heapify(li)
print('小根堆', li)
# step2: 排序
# heapq.heappop()方法彈出堆頂元素,即最小的元素
new_li = []
for i in range(10):
new_li.append(heapq.heappop(li))
print('排序后', new_li)
# output:
排序前 [3, 4, 9, 0, 5, 1, 6, 2, 8, 7]
小根堆 [0, 2, 1, 3, 5, 9, 6, 4, 8, 7]
排序后 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
堆排序實現版本2 (不考慮時間復雜度)
# 借助 heapq.heappush() & heapq.heappop()
# heappush(h, item) # Push the value item onto the heap, maintaining the heap invariant.
import heapq
def heap_sort(li):
h = []
# 建小根堆
for i in li:
heapq.heappush(h, i)
# 依次彈出最小元素構成有序列表,切片回li
li[:] = [heapq.heappop() for i in range(len(li))]
對比分析
-
heapq的heapify建堆函數和我們自己實現的建堆函數大同小異。我們自己的sift函數一步到位(實現堆頂元素選位的過程);heapify中使用兩步判斷比較,其實不如我們自己寫的sift好。
-
heappop函數實現每次彈出堆頂元素,彈出后再將整個堆調整為一個新的小根堆。它的目的是為了實現優先隊列,所以才會這樣設計。我們如果希望借助它實現列表排序,只能手動排序。
補充
heap的值可以是元組
>>> h = []
>>> heappush(h, (5, 'write code'))
>>> heappush(h, (7, 'release product'))
>>> heappush(h, (1, 'write spec'))
>>> heappush(h, (3, 'create tests'))
>>> heappop(h)
(1, 'write spec')
heapq.heapify(x)底層代碼實現
def heapify(x):
n = len(x)
for i in reversed(range(n//2)):
_siftup(x, i)
def _siftup(heap, pos):
endpos = len(heap)
startpos = pos
newitem = heap[pos]
# Bubble up the smaller child until hitting a leaf.
childpos = 2*pos + 1 # leftmost child position
while childpos < endpos:
# Set childpos to index of smaller child.
rightpos = childpos + 1
if rightpos < endpos and not heap[childpos] < heap[rightpos]:
childpos = rightpos
# Move the smaller child up.
heap[pos] = heap[childpos]
pos = childpos
childpos = 2*pos + 1
# The leaf at pos is empty now. Put newitem there, and bubble it up
# to its final resting place (by sifting its parents down).
heap[pos] = newitem
_siftdown(heap, startpos, pos)
def _siftdown(heap, startpos, pos):
newitem = heap[pos]
# Follow the path to the root, moving parents down until finding a place
# newitem fits.
while pos > startpos:
parentpos = (pos - 1) >> 1
parent = heap[parentpos]
if newitem < parent:
heap[pos] = parent
pos = parentpos
continue
break
heap[pos] = newitem
heapq.heappop底層代碼實現
def heappop(heap):
"""Pop the smallest item off the heap, maintaining the heap invariant."""
lastelt = heap.pop() # raises appropriate IndexError if heap is empty
if heap:
returnitem = heap[0]
heap[0] = lastelt
_siftup(heap, 0)
return returnitem
return lastelt