ヒープ。 パート1.二項ヒープ

こんにちは、Habrosociety!



habrには、セグメントツリー、duchaなど、多くの興味深いデータ構造の説明があります。 複雑なデータ構造に興味があるなら、catへようこそ! 私の記事シリーズでは、さまざまなタイプのヒープと、それらを実行する方法について説明します。

1) 二項パイル

2) 左ヒープ

3) フィボナッチ束

4) これらのデータ構造を実践する



問題文:

オブジェクトの多くが(タスクによって異なる)格納されるデータ構造を構築するために、各オブジェクトにはキーフィールドがあり、それによって最小要素をすばやく見つけることができます。 この構造では、次の操作が可能です。

Make



新しい空のヒープを作成し、

Insert



-ヒープに新しい要素を挿入し、

Minimum



-最小キー、

ExtractMin



最小抽出、

Merge



-2つのヒープをMerge



し、

Decrease



-キー削減、

Delete



-オブジェクトを削除します。



多くは、配列:)やバイナリヒープなど、この構造を実装する最も簡単な方法に精通しています。 それらの単純さと一般的な知識のために、私はそれらについて詳しく説明しません。



ご存知のように、 バイナリヒープの場合 、上記の操作の漸近的な動作は次のとおりです。

Make



-O(1)

Merge



-O(N)

Insert



-O(ログN)-Nはヒープ内の要素の数です。

Minimum



-O(1)

ExtractMin



-O(ログN)

Decrease



-O(ログN)

Delete



-O(ログN)



バイナリヒープのアルゴリズムについては説明しません。Habréを含め、至る所で繰り返し説明されているからです。 バイナリヒープに慣れていない人のために、読み続ける前にバイナリヒープに慣れることをお勧めします。



次に、 二項ヒープと呼ばれるより興味深いデータ構造を考えます



二項パイル

二項パイルは、いくつかの制限がある二項ツリーのセットです。 少し後で紹介します。



二項ツリーは、再帰的に定義されるツリーです。

B iはB i-1であり、ツリーB i-1は根の左息子になりました。

B 0はトップです。

B 0 、B 2 、B 3の例

画像



二項ツリー(B k )には、多くの興味深い特性があります

T.1。 2 kピーク

T.2。 木の高さk

T.3。 深さiのC i k頂点(これが二項と呼ばれる理由です:C i kは二項係数です)。

T.4。 ルートの子は、B、B k-2 、...、B 0-の順です。

T.5。 二項ツリーOの最大頂点の高さ(log N)

特性は帰納法によって証明されます。 木をよりよく理解するために、読者自身に証明を行うように勧めます。



それで、 二項ヒープに戻りましょう二項パイルは二項ツリーのセットであり、次の制限があります。

1)各二項ツリーでは、ヒーププロパティが保持されます。

2)同じサイズの2つのツリーがない

3)ツリーはサイズ順に並べられます。



二項ヒープがプログラムにどのように保存されるかについて話しましょう。 「左の息子-右の兄弟」メソッドを使用します。 ルートリスト( root_list



、その長さはroot_list.length



)を保存します。このリストには、二項式ツリーのルートが、高さの順に並んでいます。 各頂点には次のフィールドがあります。

data



一番上に保存されるデータ(これにより最小値が見つかります)

right



-右兄弟

child



-左の息子

degree-頂点の次数(明らかに、二項ヒープ内のツリーはこのフィールドによって順序付けられます)



すぐに気づく:

プロパティ H.1:

root_list.length



= O(log N)の長さ。Nはヒープ内の要素の数です。

証明のために、T.1のために、数値のバイナリ表記でツリーB kが存在することに注意するだけで十分です。



二項ヒープで実行できる操作の説明に移りましょう。



作る

タスク :空のヒープを作成します。

アルゴリズム :空のroot_list



作成します。

難易度 :明らかに、実行時間はO(1)です。



マージ

タスク :2つのヒープを1に結合します。

アルゴリズム :最初に、ヒープルートリストを1つのルートリストに結合し、次数の順序を維持します。 このアルゴリズムは、mergeSortで2つの配列をマージすることに似ています。

リストの先頭へのポインターで保存し、結果のリストに最小リストを書き込みます。書き留めたリストは次のリストに移動します。 次に、受信した新しいルートリストの最初から最後まで移動し、同じサイズのツリーを1にマージします。

1)同じサイズの2つのツリーのみ。 次にそれらを結合します。

2)同じサイズの3本の木。 最後の2つを組み合わせます。

2つのツリーを結合するときは、一方のキーの方が小さいキーを見て、もう一方のツリーをこのツリーのルートの左息子にするだけで済みます。



2つのヒープを結合した後に何が起こるかの例:

画像



難しさroot_list1.length



時間O( root_list1.length



)+ O( root_list2.length



)=(プロパティH.1による)= O(log N)。

1つのパス(O(log N))で、結合二項ツリーを取得します。 総複雑度はO(log N)であることがわかります。



挿入

タスク :ヒープに新しいアイテムを挿入します。

アルゴリズム :1つの要素の束を作成し、その束と組み合わせます。

難易度 :O(1)+ O(log(N))= O(log(N))



最低

タスク :ヒープ上の最小値を見つけます。

アルゴリズム :明らかに、最小値はルートリストにあります。つまり、それを見つけるにはルートリストを調べる必要があります。

難易度 :O( root_list.length



)= O(ログ(N))。



エキスミン

タスク :最小要素を削除します。

アルゴリズムMinimum



を使用して検索します。 ルートリストから削除します。 彼の子のインバーテッドリストから、新しいヒープ(H 1 )のroot_listを作成し、元のヒープをH 1と結合します。

難易度 :最小値を抽出する各操作はO(log N)で機能するため、O(log N)+ O(log N)+ O(log N)= O(log N)



減らす

タスク :この頂点のデータ値を減らします。

アルゴリズム :上部の値を減らします。 次に、ヒーププロパティがピークとその先祖に対して違反される可能性があります。その後、それらの場所を変更します。 ピークがその場所に「現れる」まで、このプロセスを続けます。 このアルゴリズムは、バイナリヒープのアルゴリズムと同じように機能します。

難易度 :最悪の場合、頂点はルートにポップアップします。つまり、O(log N)アクションを実行します(各ステップの頂点は1レベル高くなり、二項ツリーの高さはT.5 O(log N)です)



削除する

タスク :任意の要素を削除します。

アルゴリズム :最初に、減少を使用して、頂点の値を可能な限り最小にします。 そして、ヒープの最小値( ExtractMin



)を削除します。

難易度 :O(log N)+ O(log N)= O(log N)



おわりに

二項ヒープのデータ構造を調べ、その漸近的な動作を証明しました。

次の記事では、二項ヒープに基づいて、やや複雑なデータ構造、つまりフィボナッチヒープを構築します。



rain.ifmo.ru/cat/view.php/vis/heaps/binomial-2001で二項ヒープをいじることができます

T.Kormenの「アルゴリズム:構築と分析」で詳細を読むことができます。

ご清聴ありがとうございました。またお会いしましょう!



All Articles