コンピュータサイエンスにおいて、二項ヒープは優先度キューとして機能するデータ構造です。これは、2 つのヒープを対数時間でマージできるため、マージ可能なヒープ(メルダブルヒープとも呼ばれる) の一例です。バイナリヒープに似たヒープとして実装されますが、バイナリヒープで使用される完全バイナリツリーとは異なる特別なツリー構造を使用します。[ 1 ]二項ヒープは 1978 年にJean Vuilleminによって発明されました。[ 1 ] [ 2 ]
二項ヒープは、二項木の集合として実装されます(単一の二分木の形状を持つバイナリヒープと比較してください)。二項木は、次のように再帰的に定義されます。[ 1 ]

次数 の二項木もっているノード、高さその名前は形状に由来する。次数 の二項ツリーもっている深層ノード、二項係数。その構造により、次数 の二項ツリー2 つの木から構築できます一方の要素をもう一方のツリーのルートの最も左の子として接続することによって実現されます。この特徴は二項ヒープのマージ操作の中心であり、他の従来のヒープに対する大きな利点となっています。[ 1 ] [ 3 ]
二項ヒープは、二項ヒープの特性を満たす二項木の集合として実装されます。[ 1 ]
最初の性質は、各二項木の根が木の中で最小のキーを含むことを保証する。したがって、ヒープ全体で最小のキーは根のいずれかである。[ 1 ]
2番目の性質は、二項ヒープがノードは最大で二項木、は二進対数です。これらの木の数と次数は、ノードの数によって一意に決定されます。:数値のバイナリ表現における非ゼロビットごとに1つの二項木が存在する例えば、10進数の13は2進数では1101です。したがって、13個のノードを持つ二項ヒープは、次数3、2、0の3つの二項ツリーで構成されます(下図参照)。[ 1 ] [ 3 ]

さまざまな方法の数異なるキーを持つアイテムは、最大の奇数の約数に等しい二項ヒープに配置できます。。 のためにこれらの数字は
もしアイテムは一様ランダムな順序で二項ヒープに挿入され、これらの配置はそれぞれ等しい確率で発生します。[ 3 ]
二項木のルートノードへのランダムアクセスを必要とする操作がないため、二項木のルートは、木の昇順で並べられたリンクリストに格納できます。各ノードの子の数は可変であるため、二分木で一般的なように、各ノードがそれぞれの子に個別のリンクを持つのはうまくいきません。代わりに、各ノードから木の中で最も高い次数の子と、それより次に小さい次数の兄弟ノードへのリンクを使用してこの木を実装できます。これらの兄弟ポインタは、各ノードの子のリンクリストの次のポインタとして解釈できますが、ルートのリンクリストとは逆の順序、つまり大きい順から小さい順になります。この表現により、同じ次数の2つの木を連結して、定数時間で次の大きい次数の木を作成できます。[ 1 ] [ 3 ]

2 つのヒープをマージする操作は、他のほとんどの操作のサブルーチンとして使用されます。この手順内の基本的なサブルーチンは、同じ次数の二項ツリーのペアをマージします。これは、2 つのツリーのルートにあるキー (両方のツリーで最小のキー) を比較することによって実行できます。キーが大きい方のルート ノードは、キーが小さい方のルート ノードの子になり、その次数が 1 つ増加します。[ 1 ] [ 3 ]
function mergeTree(p, q) if p.root.key <= q.root.key return p.addSubTree(q) else return q.addSubTree(p)

2 つのヒープをより一般的にマージするには、マージ アルゴリズムと同様の方法で、両方のヒープのルートのリストを同時に走査し、ツリーのオーダーが小さいものから大きいものへと順に走査します。マージされる 2 つのヒープのうち一方にのみオーダーのツリーが含まれている場合この木は出力ヒープに移動されます。2 つのヒープの両方に次数 の木が含まれている場合2 つのツリーは、次の 1 つのツリーにマージされます。これにより、最小ヒープ特性が満たされます。後々、この木を別の次数を持つ木とマージする必要が生じるかもしれません。2 つの入力ヒープのいずれかに存在します。アルゴリズムの実行中に、最大で 3 つの任意の次数の木を調べます。2 つはマージする 2 つのヒープから、1 つは 2 つのより小さな木から構成されます。[ 1 ] [ 3 ]
function merge(p, q) while not (p.end() and q.end()) tree = mergeTree(p.currentTree(), q.currentTree())
heap.currentTree().empty()でない場合 tree = mergeTree(tree, heap.currentTree())
heap.addTree(tree) heap.next(); p.next(); q.next()
二項ヒープ内の各二項木は、そのサイズの二進表現のビットに対応するため、2 つのヒープのマージと、 2 つのヒープのサイズを右から左に二進数で加算することの間には類似性があります。加算中にキャリーが発生すると、これはマージ中に 2 つの二項木がマージされることに対応します。[ 1 ] [ 3 ]
マージ中の各二項木の走査は根のみに関係するため、かかる時間は最大でもオーダーになりますしたがって、実行時間は[ 1 ] [ 3 ]
ヒープに新しい要素を挿入するには、その要素のみを含む新しいヒープを作成し、それを元のヒープとマージするだけで済みます。マージのため、1つの挿入に時間がかかります。しかし、マージされたヒープのうち1つだけがより大きな次数を持つツリーを持つようになった時点でマージをショートカットするマージ手順を使用することで、これを高速化できます。この高速化により、一連の連続挿入の場合、挿入にかかる合計時間は別の言い方をすれば、(シーケンス内の最初の挿入の対数オーバーヘッドの後)各連続挿入の償却時間は(つまり定数)挿入ごと。[ 1 ] [ 3 ]
二項ヒープの変種であるスキュー二項ヒープは、二進数システムではなくスキュー二進数システムに基づいて木のサイズが決定されたフォレストを使用することで、最悪の場合の挿入時間を一定に保つ。[ 4 ]
ヒープの最小要素を見つけるには、二項ツリーのルートの中で最小値を見つけます。これは次のように実行できます。時間があるだけなので木の根を調べる。[ 1 ]
最小要素を含む二項ツリーへのポインタを使用することで、この操作にかかる時間を短縮できます。最小値を求める以外の操作を実行する場合は、ポインタを更新する必要があります。これは次のように行うことができます。更新ごとの時間を削減しつつ、いずれの操作の全体的な漸近実行時間も増加させない。
ヒープから最小要素を削除するには、まずその要素を見つけ、二項木のルートから削除し、その子サブツリーのリストを取得します(子サブツリーはそれぞれ異なる次数を持つ二項木です)。このサブツリーのリストを最小の次数から最大の次数に並べ替えて、別の二項ヒープに変換します。次に、このヒープを元のヒープとマージします。各ルートは最大で子供たちよ、この新しい山を作るには時間がかかるヒープのマージには時間がかかりますそのため、削除操作全体には時間がかかります。[ 1 ]
function deleteMin(heap) min = heap.trees().first() heap.trees() の各currentについて、 current.root < min.rootの場合はmin = current とし、min.subTrees()の各ツリーについて同様にする。 tmp.addTree(tree) heap.removeTree(min) merge(heap, tmp)
要素のキーを小さくすると、親要素のキーよりも小さくなり、最小ヒープの性質に違反する可能性があります。このような場合は、最小ヒープの性質に違反しなくなるまで、その要素を親要素と交換し、場合によっては祖父母要素とも交換します。各二項木の高さは最大でなので、これには時間。[ 1 ]ただし、この操作では、ツリーの表現に各ノードからツリー内の親へのポインタを含める必要があり、他の操作の実装がやや複雑になります。[ 3 ]
ヒープから要素を削除するには、そのキーを負の無限大(または同等に、ヒープ内のどの要素よりも小さい値)に減らし、ヒープ内の最小値を削除します。[ 1 ]
以下に、さまざまなヒープ データ構造の時間計算量[ 5 ]を示します。略語am.は、与えられた計算量が償却済みであることを示し、そうでない場合は最悪の場合の計算量です。「 O ( f )」および「Θ ( f )」の意味については、ビッグ O 記法を参照してください。操作名は、最小ヒープを前提としています。