it

ヒープとは?データ構造の意味と仕組み(二分ヒープ:最大ヒープ・最小ヒープ:優先度付きキュー:完全二分木など)

当サイトでは記事内に広告を含みます

プログラミングやアルゴリズムを学ぶ上で「ヒープ」というデータ構造は欠かせない知識のひとつです。

「ヒープって名前は聞いたことあるけど、どういう構造なの?」と疑問をお持ちの方も多いのではないでしょうか。

この記事では、ヒープとは何か、データ構造としての意味と仕組みについて、二分ヒープ・最大ヒープ・最小ヒープ・優先度付きキューとの関係まで詳しく解説しています。

データ構造の基礎を固めたい方はぜひ参考にしてください。

ヒープとは?データ構造としての基本的な意味と定義

それではまず、ヒープの基本的な意味と定義について解説していきます。

ヒープとは、完全二分木という木構造をベースに持ち、親ノードが常に子ノード以上(または以下)の値を持つという「ヒープ条件」を満たすデータ構造のことです。

コンピュータサイエンスにおけるヒープは、最大値または最小値を素早く取り出すことに特化した、優先度付きキューの実装に使われることが多いデータ構造です。

なお、プログラミングにおけるメモリ領域としての「ヒープ(heap領域)」とは異なる概念です。

完全二分木とはどういう構造か?

ヒープのベースとなる「完全二分木」とは、最下層を除くすべての層がノードで埋まっており、最下層のノードは左から順に詰まっている二分木のことです。

この構造のおかげで、ヒープは配列(1次元のリスト)で効率よく表現することができます。

配列でのヒープ表現では、インデックスiの親ノードは(i-1)÷2、左の子ノードは2i+1、右の子ノードは2i+2で計算できます。

配列でのヒープ表現の例(最大ヒープ):

配列:[9, 7, 5, 3, 6, 2, 4]

インデックス0(9)→ 根

インデックス1(7)→ 9の左の子

インデックス2(5)→ 9の右の子

インデックス3(3)→ 7の左の子

インデックス4(6)→ 7の右の子

最大ヒープと最小ヒープの違い

ヒープには最大ヒープと最小ヒープの2種類があります。

種類 ヒープ条件 根(ルート)の値 主な用途
最大ヒープ 親 ≥ 子(親は子以上の値) 全体の最大値 最大値の高速取得・ヒープソート
最小ヒープ 親 ≤ 子(親は子以下の値) 全体の最小値 優先度付きキュー・ダイクストラ法

ヒープの主な操作と計算量を解説

続いては、ヒープの主な操作と計算量を確認していきます。

ヒープはいくつかの基本的な操作を効率よく行えることが特徴です。

挿入操作(Insert)の仕組み

ヒープへの要素の挿入は次の手順で行います。

まず新しい要素を配列の末尾に追加し、次に親ノードと比較しながら適切な位置まで上昇させる「上向き調整(Sift Up)」を行います。

挿入操作の計算量はO(log n)であり、木の高さに比例した効率的な操作です。

最大値(または最小値)の取り出し操作の仕組み

ヒープの最大の特徴は、最大値(最大ヒープの場合)を常にO(1)で参照できることです。

取り出し(削除)の場合は根を取り出した後、末尾要素を根に移動させ、下向き調整(Sift Down)でヒープ条件を回復します。

取り出し操作の計算量はO(log n)です。

ヒープの構築(Build Heap)の計算量

n個の要素からヒープを構築する際、単純に1要素ずつ挿入するとO(n log n)かかりますが、末尾の親ノードから順に下向き調整を行う「一括構築」ではO(n)で実現できます。

これはヒープソートやヒープを使うアルゴリズムの効率化に重要な性質です。

優先度付きキューとしてのヒープの活用

続いては、優先度付きキューとしてのヒープの活用を確認していきます。

ヒープの最も代表的な応用が「優先度付きキュー(Priority Queue)」の実装です。

優先度付きキューとは、通常のキュー(先入れ先出し)と異なり、優先度の高い要素から取り出せるデータ構造です。

最小ヒープを使うことで、常に優先度が最も高い(値が最小の)要素をO(log n)で取り出せる優先度付きキューを実装できます。

ダイクストラ法でのヒープの活用

グラフの最短経路を求めるダイクストラ法では、次に処理すべき最小コストのノードを効率よく取り出すために最小ヒープ(優先度付きキュー)が使われます。

ヒープを使うことでダイクストラ法の計算量をO((V+E) log V)(Vは頂点数、Eは辺数)に抑えることができます。

Pythonのheapqモジュールでのヒープ活用例

Pythonでのヒープ(最小ヒープ)の使用例:

import heapq

heap = []

heapq.heappush(heap, 5) # 要素の挿入

heapq.heappush(heap, 1)

heapq.heappush(heap, 3)

print(heapq.heappop(heap)) # 最小値1を取り出す

まとめ

この記事では、ヒープとは何か、データ構造としての意味と仕組みについて、二分ヒープ・最大ヒープ・最小ヒープ・優先度付きキューへの応用まで詳しく解説しました。

ヒープは最大値・最小値を効率よく取り出せるデータ構造であり、ソートや最短経路探索など多くのアルゴリズムの基盤となっています。

今回の内容を参考に、ヒープの仕組みと活用方法をしっかり身につけていきましょう。