ヒープ(優先度付きキュー)
1. ヒープ(優先度付きキュー,二分木)
(1) 概要
データ群内から 最小値を抽出する操作をO(1)で行う → 何度も最小値を抜き出す問題に適している
データ群内にデータを追加する操作をO(logN)で行う
最大値の抽出は直接は使えない.各データを(-1)倍して使う.
最小値を参照したいだけのときは,抽出して,追加する. 0番目の要素を参照する.
(2) 実装方法
heapqモジュールを使う(ちょっと癖があります)
(3) 実装例
code:python
import heapq
heapq.heapify(a) # リストaをヒープ化.
heapq.heappush(a, 11) # 11を追加
heapq.heappush(a, 1) # 1を追加
x = heapq.heappop(a) # 最小値(0)を取り出す
x = heapq.heappop(a) # 最小値(1)を取り出す
2. 拡張版 ヒープ
(1) 任意の要素削除付きヒープ
ヒープでは最小値をO(1)で抽出できるが,それ以外の要素は取り出し(削除)不可能
辞書を併用して,疑似的に任意の要素の削除も可能にする.みなしO(logN)
(癖のある)heapqを元に,オブジェクト指向型に変更
初期化:LazyHeapクラスを作成.引数にリストを与えると,heapq.heapfyと同じ働き
push(x):xを追加
pop(): 最小値を抽出.ヒープから削除される
get(): 最小値を獲得.ヒープから削除されない
remove(x):(最小値とは限らない)xを削除
lenでヒープ内のデータ数を獲得
x in でヒープ内にデータxが存在するかを True/Falseで判定可能.O(1)
printでヒープの中身を表示できるが,removeで削除したデータも残っているので注意
code: python
import heapq
from collections import defaultdict
class LazyHeap():
def __init__(self, init_arr=[]):
self.heap = []
self.members = defaultdict(int)
self.removed = defaultdict(int)
self.len = 0
for init_element in init_arr:
heapq.heappush(self.heap, init_element)
self.len += 1
def __len__(self):
return self.len
def push(self, k):
heapq.heappush(self.heap, k)
self.len += 1
def pop(self):
self._clear()
self.len -= 1
return heapq.heappop(self.heap)
def get(self):
self._clear()
def _clear(self):
while True:
heapq.heappop(self.heap)
else:
return
def remove(self, k):
self.len -= 1
def __str__(self):
return str(self.heap)
def __contains__(self, item):
return True if self.membersitem > 0 else False # 使用方法
print(len(hq)) # 6(ヒープ内の要素数)
hq.push(11) # 11を追加
hq.push(1) # 1を追加
print(len(hq)) # 8(ヒープ内の要素数)
x = hq.pop() # 最小値(0)を取り出す
x = hq.pop() # 最小値(1)を取り出す
print(len(hq)) # 6(ヒープ内の要素数)
x = hq.get() # 最小値(2)を獲得する(取り出さない)
print(len(hq)) # 6(ヒープ内の要素数は減っていない)
print(2 in hq) # True(2はヒープ内に存在する)
x = hq.remove(4) # (最小値でない)4を削除する
print(len(hq)) # 5(ヒープ内の要素数は正しく減っている)
print(4 in hq) # False(4はヒープ内に存在しない)
x = hq.pop() # 最小値(2)を取り出す
print(len(hq)) # 4(ヒープ内の要素数は正しく減っている)
print(4 in hq) # False(4はヒープ内に存在しない)
x = hq.pop() # 最小値(4? 6?)を取り出す
print(x) # 6(正しい!)
print(len(hq)) # 3
3. 番外編
C++にはstd::setやstd::multisetなどがある
これらを使うと
値の追加,削除,検索:$ O(log(n))
max/minの取得:$ O(1)
で出来るっぽい
Pythonの標準ライブラリには無さそうなので,これらを使いたかったら自力で(2. 拡張版ヒープのように)頑張るか,C++を使うと良さそう
勿論,C++にも優先度付きキュー(std::priority_queue)はある