ヒープは、すべての親がヒープ プロパティ、つまり子よりも常に小さい (min-heap) か、常に大きい (max-heap) のいずれかを満たしている完全なバイナリ ツリーです。 「完了」とは、おそらく最後のレベルを除いて、すべてのレベルがいっぱいであることを意味し、左から右に埋められます。
1 Min-heap
/ \
3 5
/ \
7 4
これは完全なので、フラット配列に保存します。ポインターは必要ありません。インデックス i の場合: 左の子 = 2i + 1、右の子 = 2i + 2、親 = (i - 1) // 2。
|プロパティ |最小ヒープ |最大ヒープ | |----------|----------|----------| |ルート |最小要素 |最大の要素 | |親≤子供? | ✅ はい | ❌ いいえ (≧) | |抽出すると |最小 |最大 |
Python の heapq は、デフォルトでは 最小ヒープ です。最大ヒープの場合、挿入時に値を否定し、抽出時に再度否定します。
挿入 - 最後まで押してからバブルアップ (小さいうちに親と入れ替えます):
import heapq
h = [1, 3, 5, 7]
heapq.heappush(h, 2) # h becomes [1, 2, 5, 7, 3]
最小の抽出 - ルートを最後の要素と交換し、最後にポップし、下に移動 (より小さい子と交換):
smallest = heapq.heappop(h) # returns 1
木の高さは log n であるため、両方の操作は O(log n) で実行されます。
heapq.heapify(arr) を呼び出すと、リストは O(n log n) ではなく O(n) で インプレース ヒープに変換されます。これはボトムアップで機能し、各非リーフ ノードをふるい分けします。これは、CS における最も驚くべき複雑さの結果の 1 つです。
優先キューを使用すると、常に最も優先度の高い項目に最初にアクセスできます。挿入と抽出の両方が O(log n) であるため、ヒープは頼りになる実装です。 「これまでで最高のアイテムをください」というときにいつでも使用してください。
ログイン ディスカッションに参加
import heapq
def top_k(nums, k):
return heapq.nlargest(k, nums)
You need the 5 largest scores from 1 million entries.ヒープのタイプとサイズは何ですか?
Lower half (max-heap): [1, 2, 3] ← max = 3
Upper half (min-heap): [4, 5, 6] ← min = 4
Median = (3 + 4) / 2 = 3.5
ヒープは、会議室 (最も早い終了時刻を追跡)、クールダウンのあるタスクのスケジューリング、または CPU ジョブ キューなどのスケジューリングで威力を発揮します。重要な洞察は、ヒープが「次に何が終了するか」を効率的に追跡するということです。
|必要 |最良の選択 |なぜ | |------|---------------|-----| |最小/最大抽出の繰り返し |ヒープ | O(log n) 挿入 + 抽出 | |静的データ、ワンタイムソート |ソートされた配列 | O(n log n) 回、O(1) アクセス | |範囲クエリ + 注文統計 |バランスBST |すべてに対して O(log n) | |ストリームからの Top-K |サイズ K のヒープ | O(n log k) 合計 |
最小ヒープ内で O(1) となるのはどの操作ですか?
|操作 |時間 | |-----------|------| |挿入 | O(log n) | |最小/最大を抽出 | O(log n) | |ピークの最小値/最大値 |お(1) | |ヒープファイ | O(n) | | n 個の項目から上位 K 個 | O(n log k) |