[トイレに座ってアルゴリズムを読む] アルゴリズム 8: 賢い隣接リスト (配列の実装)

[トイレに座ってアルゴリズムを読む] アルゴリズム 8: 賢い隣接リスト (配列の実装)

前回は、空間と時間の複雑さがともにN 2であるグラフの隣接行列保存方法を紹介しました。今回は、グラフを保存するための別の方法である、空間と時間の複雑さが M である隣接リストを紹介します。疎グラフの場合、M はN 2 よりもはるかに小さくなります。まず、データは以下のとおりです

  1. 4 5
  2. 1 4 9
  3. 4 3 8
  4. 1 2 5
  5. 2 4 6
  6. 1 3 7

最初の行には 2 つの整数 nm が含まれています。 n は頂点の数(頂点には 1 から n までの番号が付けられます)を表し、m は辺の数を表します。次の m 行は、各行に 3 つの数値 xyz があり、頂点 x から頂点 y への辺の重みが z であることを示しています。下の図は、リンク リストを使用して隣接リストを実装する方法を示しています。

上記の実装では、グラフ内の各頂点 (左側) に対して単一リンク リスト (右側) が作成されます。このようにして、各頂点のリンク リストをトラバースすることで、各頂点のすべてのエッジを取得できます。リンク リストを使用して隣接リストを実装することは、ポインターを嫌う人にとっては悪夢です。ここでは、配列を使用して隣接リストを実装する別の方法を紹介します。これは、実際のアプリケーションで実装するのが非常に簡単な方法です。このメソッドは、各頂点 i (i の範囲は 1 から n) に対して「リンク リスト」のようなものも保存します。このリストには、次のように、頂点 i から始まるすべてのエッジが格納されます。

まず、各エッジに読み取られる順序 (1 から m) で番号を付けます。たとえば、最初のエッジ「1 4 9」には番号 1 が付けられ、エッジ「1 3 7」には番号 5 が付けられます。

ここで、3つの配列u、v、wは各エッジの特定の情報を記録するために使用されます。つまり、u[i]、v[i]、w[i]は、i番目のエッジが頂点u[i]から頂点v[i](u[i]àv[i])までのものであり、その重みがw[i]であることを示します。

最初の配列を使用して、各頂点の 1 つの辺の番号を格納します。そうすれば、後で各頂点のすべての辺を列挙できます (1 つの辺の番号を保存するだけで十分かと疑問に思うかもしれません。それは不可能です。各頂点は、そのすべての辺の番号を保存する必要があります。心配しないでください。読み続けてください)。たとえば、頂点 1 にエッジ "1 4 9" (このエッジの番号は 1) がある場合、first[1] の値は 1 に設定されます。頂点iにその頂点から始まる辺がない場合、first[i]の値は-1に設定されます。では、操作方法を見てみましょう。初期状態は以下のとおりです。

はぁ?上の図に次の配列が追加されているのはなぜですか? その機能は何ですか?心配しないでください。後で説明します。最初のエッジ「1 4 9」を読んでください。

最初のエッジ(1 4 9)を読み取り、このエッジの情報をu[1]、v[1]、w[1]に格納します。同時に、このエッジに番号が割り当てられます。このエッジは最初に読み込まれ、u、v、w 配列のインデックス 1 のセルに格納されるため、番号は 1 になります。このエッジの開始点は頂点1なので、first[1]の値を1に設定します。

さらに、この「番号1のエッジ」は、頂点番号1(つまりu[1])を開始点とする最初のエッジなので、next[1]の値は-1に設定する必要があります。つまり、現在の「番号 i のエッジ」が u[i] を開始点として最初に見つかったエッジである場合、 next[i] の値は -1 に設定されます (この next 配列は非常に神秘的であるようです⊙_⊙)。

2番目のエッジ(4 3 8)を読み取り、このエッジの情報をu[2]、v[2]、w[2]に格納します。このエッジの番号は2です。このエッジの開始頂点は頂点4なので、first[4]の値を2に設定します。さらに、この「番号2のエッジ」は頂点4を開始点として見つかった最初のエッジなので、next[2]の値は-1に設定されます。

3番目のエッジ(1 2 5)を読み取り、このエッジの情報をu[3]、v[3]、w[3]に格納します。このエッジの番号は3で、開始頂点は頂点1です。頂点 1 にはすでに「番号 1 の辺」があることがわかります。first[1] の値を 3 に設定すると、「番号 1 の辺」は失われませんか?解決策はあります。next[3]の値を1に設定するだけです。これで、次の配列が何に使用されるかがわかりました。 next[i]には、「番号iのエッジ」の「前のエッジ」の番号が格納されます。

4番目のエッジ(2 4 6)を読み取り、このエッジの情報をu[4]、v[4]、w[4]に格納します。このエッジの番号は4で、開始頂点は頂点2なので、first[2]の値を4に設定します。さらに、この「番号4のエッジ」は頂点2を開始点として見つかった最初のエッジなので、next[4]の値は-1に設定されます。

5番目のエッジ(1 3 7)を読み取り、このエッジの情報をu[5]、v[5]、w[5]に格納します。このエッジの番号は5で、開始頂点は再び頂点1です。このとき、first[1]の値を5に、next[5]の値を3に設定する必要があります。

この時点で、頂点 1 のすべての辺をトラバースするのは非常に簡単です。頂点1の辺の1つの番号がfirst[1]に格納されます。残りのエッジは次の配列を通じて見つけることができます。下の写真をご覧ください。

注意深い生徒は、頂点エッジをトラバースするときのトラバース順序が、読み取り時の順序とまったく逆であることに気付くでしょう。各頂点にエッジを挿入する場合、そのエッジは「リンク リスト」の末尾ではなく先頭に直接挿入されるためです。しかし、これによって問題が発生することはありません。それがこの方法の優れた点です。

隣接リストを作成するコードは次のとおりです。

  1. 整数n、m、i;
  2. //u、v、wの配列サイズは実際の状況に応じて設定する必要があり、mの最大値より1大きくする必要があります。  
  3. int u[ 6 ],v[ 6 ],w[ 6 ];
  4. //firstとnextの配列サイズは実際の状況に応じて設定する必要があり、nの最大値より1大きくする必要があります。  
  5. int最初[ 5 ]、次[ 5 ];
  6. scanf( "%d %d" ,&n,&m);
  7. // 最初の配列の添え字 1~n の値を -1 に初期化します。これは、頂点 1~n には今のところ辺がないことを示します。  
  8. (i= 1 ;i<=n;i++)の場合
  9. 最初[i]=- 1 ;
  10. (i= 1 ;i<=m;i++)の場合
  11. {
  12. scanf( "%d %d %d" ,&u[i],&v[i],&w[i]); //各エッジを読み取ります 
  13.      //次の2つの文が鍵となる 
  14. 次[i] = 最初[u[i]];
  15. 最初[u[i]]=i;
  16. }

次に各エッジをどのようにトラバースするのでしょうか?前に述べたように、最初の配列には実際には各頂点i ( i の範囲は1からn )の最初の辺が格納されます。たとえば、頂点1の最初のエッジはエッジ番号5 ( 1 3 7 ) であり、頂点 2 の最初のエッジはエッジ番号4 ( 2 4 6 ) であり、頂点3 には出力エッジがなく、頂点4の最初のエッジはエッジ番号2 ( 2 4 6 ) です。では、頂点1のすべての辺をどのように走査するのでしょうか?非常にシンプルです。次の図をご覧ください。

頂点1のすべてのエッジをトラバースするコードは次のとおりです。

  1. k=first[ 1 ]; // 頂点1の辺の1つの番号(実際には***によって読み込まれた辺)  
  2. while (k!=- 1 ) //残りのエッジは次の配列で順番に見つかります 
  3. {
  4. printf( "%d %d %d\n" ,u[k],v[k],w[k]);
  5. k = 次[k];
  6. }

各頂点すべてのを走査するコードは次のとおりです

  1. (i= 1 ;i<=n;i++)の場合
  2. {
  3. k=最初[i];
  4.     一方(k!=- 1 )
  5. {
  6. printf( "%d %d %d\n" ,u[k],v[k],w[k]);
  7. k = 次[k];
  8. }
  9. }

隣接リストを使用してグラフを格納する場合の時間と空間の計算量は O( M ) であり、各エッジを走査する場合の時間の計算量もO( M ) であることがわかります。グラフが疎な場合、 M はN 2よりもはるかに小さくなります。したがって、スパース グラフを格納するには、隣接行列よりも隣接リストを使用する方がはるかに適切です。

オリジナルリンク: http://ahalei.blog..com/4767671/1391988

<<:  興味深い記事:女の子を追いかけるためのさまざまなアルゴリズムを教える

>>:  人気ゲーム2048 - AIプログラムアルゴリズム分析

ブログ    
ブログ    
ブログ    

推薦する

大学を解雇され、Facebookも拒否した大物音声エンジニアのダニエル・ポーヴィー氏が、中国のXiaomiに入社する

昨日、小米集団の創業者、雷軍氏は微博で、音声認識とAIの国際的専門家であり、音声認識オープンソースツ...

自動化の将来はどうなるのでしょうか?

自動化ほど製造業界に大きな影響を与え、破壊的な影響を与えたテクノロジーはほとんどありません。自動化の...

...

MATRIX: 社会シミュレーションは、GPT4よりも配慮した大規模なモデル値の自己整合を促進します

ChatGPT などのモデルは、人間のフィードバックからの強化学習 (RLHF) に依存しており、注...

消費者がリアルなAIを信頼しない理由

Amazon Alexaのような音声アシスタントの台頭にもかかわらず、人々は本物そっくりのAIに不安...

...

画像も感情を伝えることができるのでしょうか?ロチェスター大学のチームが新しいコンピュータービジョンのタスクを提案

画像スタイルの転送?声の感情移入?いいえ、それはイメージの感情的な伝達です。コンピュータビジョンの分...

...

製造業における AI 活用事例 10 選

製造企業は、ビジネスのやり方を合理化し、効率を高めるために人工知能に注目しています。一般的な使用例を...

AIGC: 将来は誰が支払うのでしょうか?

情報獲得に対する私たちの執着は、初期の人類が生き残り、繁殖するための適応特性を発達させたことにまで遡...

顔認識情報セキュリティは大きな注目を集めており、専門家の代表者らは多くの提案を行っている。

近年、人工知能技術の成熟に伴い、顔認識の応用範囲はますます広がっています。 「顔スキャン」は、効率、...

AIエージェントに完全な人生を与えましょう! HKU NYU Xie Sainingらによる最新の知的研究:仮想は現実である

より強力な AI エージェントを構築するにはどうすればよいでしょうか?答えは、彼らに完全で現実的な世...

世界初の「サイボーグ」が死んだ!さようなら、ピーター 2.0

2020年、ピーター・スコット・モーガン博士はインターネットで話題になりました。人気の検索タイトル...