欲張りアルゴリズム-ダイクストラ最適化

前書き

ダイクストラアルゴリズムの擬似コードの7行目:d(v)を更新するたびに、最小のd(v)を見つけます。この場所でどの方法が使用されているかはわかりません。最小の方法を見つけるためにさまざまな方法が使用されます。データ構造の時間計算量も異なります。
最も直接的で暴力的なアイデアは、forループをトラバースして最小のd(v)を見つけることですが、時間計算量が高すぎるため、すべてのポイントをトラバースする必要があります。合計時間計算量はO(n 2)O(n ^ 2)O n2ポイントが多い場合、このアルゴリズムは非常に遅くなります。
したがって、最小値を見つける方法を最適化する必要があります。

ダイクストラ最適化

d(v)を格納するためのデータ構造が何をサポートする必要があるかを見てみましょう。ダイクストラアルゴリズムでは、値の挿入、最小値の抽出、値の更新、削除の機能をサポートする必要があり、変更の順序を動的に調整する必要があります。

優先キュー(PQ)は、上記の要件を満たすことができます。

一般的な優先度キューアプリケーションのシナリオ:
ダイクストラの最短パスアルゴリズム
プリムのMSTアルゴリズム
ハフマンコーディング
A ∗検索アルゴリズム
ヒープソート

優先度キューは、d(v)値、つまりPQセットを格納するために使用されます(優先度はノードに対応するd(v)値であり、その後、key(v)が均一に使用されます)。 、キューの挿入操作は、ループ内で必要な最小のKey(v)を取得するためにn回実行され、キュー内のkey(v)を更新するのにm回かかります(mはエッジの数です)。 key(v)が更新された場合、キューの調整が必要になる場合があります。(ExtractMinには、最小値の取得とキューからの削除の2つの操作が含まれます)

擬似コードは次のとおりです
ここに画像の説明を挿入
。分析の結果、アルゴリズムの時間計算量への主な影響は、キューの3つの操作、Insert、ExtractMin、およびDecreaseKeyであることがわかりました。通常の順序付けされていない配列またはリンクリストを使用して形成する場合優先キュー、次に各挿入操作複雑さはO(1)のみですが、順序付き配列またはリンクリストの場合、ExtractMinはO(n)である必要があります(配列、リンクリストをトラバースする必要があるたび)。挿入にはO(n)が必要です(適切な挿入位置を見つけるためにトラバースします)。ExtractMinはO(1)のみを使用します。したがって、キューを構築するために異なるデータ構造を選択すると、アルゴリズムの時間計算量が異なります。次の表に、キューを実装するための4つの異なるデータ構造メソッドを示します。
ここに画像の説明を挿入

一般的な実際の問題では、エッジの数m>点の数n

最小ヒープ(バイナリヒープ)

最小ヒープは、任意のノードの値が常にその子ノードの値以下であり、ヒープは完全なバイナリツリーである必要があります。実現は、リンクリストまたは配列の2つの方法で実行できます。

ダイクストラのアルゴリズムに含まれる3つの操作に焦点を当てましょう。最小ヒープによって、これら3つの操作の時間計算量が減少するのはなぜですか。

  • 挿入:ヒープへの要素の挿入には2つのステップが含まれます.1つ目は挿入(要素をリンクリストの最後または配列の最後に接続する)であり、2つ目はメンテナンス(要素の位置を調整して新しいヒープ)。調整プロセスは、比較交換の数に関連していることがわかります。新しく挿入されたノードが親ノードよりも大きい場合は、交換する必要はありません。親ノードよりも小さい場合は、2つの位置は次のようになります。交換されます。プロセス中は、親ノードが他の子ノードよりも小さい必要があるため、親ノードのみを比較する必要があります。最大でlogn回比較されているため、Insertの複雑さはOです。 (ログ)。

例:挿入(7)
ここに画像の説明を挿入

  • ExtractMin:前述のように、2つのステップがあります。1つはヒープの最上位(つまり、最小の要素)に戻ること、もう1つはヒープの最上位の要素を削除する(トラバースされたポイントのセットに追加する)ことです。 )、およびヒープの最上位に直接戻ることの複雑さはO(1)であり、ヒープの最上位要素を削除した後、ヒープを維持するのにかかる時間に焦点が当てられます。ヒープの一番上の要素を直接削除し、その左右の子ノードから小さい方の要素を親ノードとして選択すると、最終的にヒープが形成されない可能性があります。つまり、ヒープを一度再構築する必要があります。ただし、最初にtop要素とtail要素を交換してから削除すると、ヒープを保持できます。

例:ExtractMin()
ここに画像の説明を挿入

  • DecreaseKey:キー値を更新し、ノードを比較します。キー値が更新された場合、親ノードと比較して挿入操作に似ていますが、時間計算量は依然としてO(logn)です。

例:DecreaseKey(ptr、7)
ここに画像の説明を挿入

まとめると、最小ヒープを使用して優先度付きキューを実装する場合の時間計算量は、
O(n ∗ logn(n回I nsert)+ n ∗ logn(n回E xtract M in)+ m ∗ logn(m回Kを減らす)です。 ey))= O(mlogn)O(n * logn(n回Insert)+ n * logn(n回ExtractMin)+ m * logn(m回DecreaseKey))= O(mlogn)O nl o g n nI n s e r t +nl o g n nE x t r a c t M i n +mL O G N MD E C R E A S E K用のE Y =O m l o g n

二項ヒープ(二項ツリー)

二項ヒープは、二項ヒープに基づいて、ヒープのマージに費やされる時間を最適化します(O(n)からO(logn)へ)。
バイナリヒープは単一のツリーに格納されますが、バイナリヒープでは複数のツリーが使用されます。

二項式ツリーの定義:

  • 単一のノードを二項ヒープとして使用できます
  • 二項ヒープは、ルールに従って同じサイズの2つの二項ツリーで構成されている必要があります(ツリーの小さい方のルートを他のツリーのルートノードの下に接続します)

例:下の写真は、複数の二項ツリーを示しています。曲がっているように見えますが、実際には非常にまっすぐです。それぞれの木を順番に見ると、二項式と陽慧三角形であるため、二項式の木と呼ばれています。
1
1 1
1 2 1
1 3 3 1

ここに画像の説明を挿入
二項ヒープは複数の二項ツリー(フォレスト)で構成され、ノードを1つだけ含む二項ツリーはB 0B_0と呼ばれますB0、2つ置くB 0 B_0B0結合された二項ツリーはB1B_1と呼ばれますB1、 等々。各二項ツリーのルートノードをリンクリストに接続することは、二項ヒープです。(Bはレベルです)

ヒープ内に同じレベルの2つのツリーが存在することはできません。同じレベルの2つのツリーがヒープ内に表示される場合、それらはマージされます。したがって、二項ヒープにn個のツリーがある場合、マージ後に最大でlog 2 n + 1 log_2 n +1になります。l o g2n+1本の木、最も高い木の高さはlog 2 n log_2 nl o g2n

二項ヒープの時間計算量を簡単に分析してみましょう。

  • 連合
    • 同じサイズの木はありません:それらをまとめます
    • 同じサイズのツリーがあります。同じサイズがなくなるまで同じサイズをマージし、2つの二項ツリーが1つのコストO(1)になり、フォレストをマージするツリーの総数がO(logn)を超えないようにします。
  • インサート
    • 元の二項パイルにB0B_0がない場合B0、直接入れて
    • はいの場合、2つのB 0 B_0B0構成B1 B_1B1 、まだある場合はマージを続行し、コストO(logn)
  • 最小抽出
    • 最初に最小値を取得し、lognツリーO(logn)以下のルートノードを比較します。ルートを削除した後、二項ツリーは2つの二項ツリーに分割され、他のツリーとマージされる可能性があります。マージは引き続きOです。 (logn)。)、合計時間O(logn)

二項ヒープは、二項ヒープと比較して、ユニオン演算の時間計算量を削減することがわかります。

二項ヒープの挿入とユニオンの時間が記録されます。実際、その時間計算量がO(1)に達する可能性があることをさらに確認できます。
例を見てみましょう。前回複数のマージでO(logn)が使用された後、次回はO(1)のみが使用されます。
ここに画像の説明を挿入
ある時点でO(logn)のコストがかかると仮定すると、その後連続して要素を挿入するのにかかる時間は、明らかにO(1)、O(2)、O(1)、O(3)、O(1)です。 。長い間、挿入にかかる時間は一定であることがわかりました。したがって、O(logn)のみの時間消費に基づいて、時間計算量がO(logn)であると単純に断言することはできません。一連の操作の全体的な予想時間計算量を考慮する必要があります。償却分析により、O(1)であることが証明できます。

総括する

優先度付きキューと言えば、バイナリヒープとバイナリヒープの2つの新しいデータ構造が導入されました。これらの設計は、データ構造からアルゴリズムを最適化する、アルゴリズム設計プロセスのもう1つの最適化です。

  • バイナリヒープ
    はキューの最小値をすばやく取得でき、挿入と削除の速度が速い
  • バイナリヒープバイナリヒープ
    に基づいて、ヒープのマージおよび挿入操作にかかる時間を短縮します。

最初から、線形リンクリストを使用して優先度付きキューを実装しましたが、最小値が遅すぎることがわかり、順序付きリンクリストに変更され、挿入が遅すぎることがわかりました。その後、バイナリヒープを使用して部分的に順序付けされ、次に1つのツリーのみを使用することから複数のツリーを使用するように変更されましたツリーの二項ヒープは、制限条件を徐々に緩和し、多くの冗長な計算を減らし、問題を最適化します。

おすすめ

転載: blog.csdn.net/qq_32505207/article/details/108316547