隣接リストとグラフの行列表現

⚡ スマートサマリー

グラフの隣接リスト表現と隣接行列表現は、頂点と辺をメモリに格納し、アルゴリズムがネットワークを走査できるようにする。隣接リストは頂点ごとにリンクリストを使用するのに対し、隣接行列は正方形の2次元グリッドを使用する。

  • 📐 隣接リスト: V個の連結リストの配列で、インデックスiの各リストには頂点iに隣接するすべての頂点が格納され、O(V + E)のメモリ容量が必要となる。
  • おいおいおいそ️ 隣接行列: AV × V は2次元配列で、matrix[i][j] にはエッジの重みが格納され、頂点 i と頂点 j の間にエッジが存在する場合は 1 になります。
  • ⚡ 検索速度: 隣接行列は「iとjの間にエッジが存在するか?」という問いにO(1)の時間で答えるのに対し、隣接リストは隣接リストをスキャンするためにO(次数)の時間を必要とする。
  • 💾 メモリ: 隣接行列は疎グラフの場合でも常にO(V²)のメモリを消費するのに対し、隣接リストは実際のエッジ数に応じてスケーリングします。
  • 🔍 最適: エッジクエリが頻繁に発生する密なグラフには隣接行列を、走査負荷の高い疎なグラフには隣接リストを選択してください。
  • 🛠️ 用途: これらの表現はどちらも、AIシステム全体で使用されているBFS、DFS、ダイクストラ法、PageRank、道路網ルーティング、グラフニューラルネットワークのパイプラインを支えています。

隣接リストとグラフの行列表現

見た目は違っても、みんな グラフの種類 同様の方法で表現できます。グラフ表現には一般的に2種類あります。

  1. 隣接行列
  2. 隣接リスト

隣接リスト

隣接リストは連結リストで構成されます。各頂点は配列のインデックスとみなされ、各要素は連結リストを表します。これらの連結リストには、インデックス頂点と辺を共有する頂点が含まれます。

隣接リストの例を以下に示します。

隣接リスト

グラフに頂点がV個、辺がE個あるとする。隣接リストの空間計算量は O(V + E)これは、考えられるすべての頂点のペアではなく、実際のエッジの数に応じてスケーリングされます。

最悪の場合の空間計算量は O(V²) 与えられたグラフが完全グラフである場合、すべての頂点が他のすべての頂点と接続されます。

隣接行列

隣接行列は2次元配列で構成されます。頂点数がVのグラフの場合、行列のサイズは次のようになります。 V × V.

いう matrix[i][j] = 5これは、ノード i とノード j の間に重みが 5 のエッジが存在することを意味します。

次のグラフとその隣接行列を見てみましょう。

隣接行列

私たちは 2D配列 次の手順を使用します。

ステップ1) 頂点Aは頂点Bと直接辺で結ばれており、その重みは5です。したがって、A行B列のセルには5が入ります。A行の残りのセルには0が入ります。

ステップ2) 頂点Bは頂点Cと直接辺で結ばれており、その重みは4です。したがって、B行C列のセルには4が入ります。Bは他のどのノードにも出ていないため、B行の残りのセルには0が入ります。

ステップ3) 頂点Cは他のどの頂点とも直接的な辺を持たない。したがって、行Cはすべてゼロで埋められる。

ステップ4) 頂点Dは、頂点Aおよび頂点Cと有向辺で結ばれている。

  • D行A列のセルには7の値が入ります。D行C列のセルには2の値が入ります。
  • 行 D の残りのセルはゼロで埋められます。

ステップ5) 頂点EはBおよびDと有向辺で結ばれています。E行B列のセルには6の値が入ります。E行D列のセル​​には3の値が入ります。E行の残りのセルには0が入ります。

注意すべき点がいくつかあります。

  • 隣接行列の主対角線が0の場合、グラフには自己ループは存在しない。
  • グラフは、(a, b)と(b, a)のセルが同じ値を持たない場合に有向グラフとなる。そうでない場合は無向グラフとなる。
  • いずれかのセルの値が1より大きい場合、そのグラフは重み付きグラフである。

隣接行列の主な問題点は、正方形のメモリ領域を必要とすることである。存在しないエッジであっても、メモリ上にセルを割り当ててしまう。

例えば、100個のノードを持つグラフがある場合、それを格納するために10,000個のセルが必要になります。 RAMグラフのエッジが少ない場合、このような大きなメモリを割り当てるのは無駄になる可能性があります。したがって、隣接行列を使用した空間計算量は次のようになります。 O(N²)ここで、Nはグラフ内のノード数である。

隣接リストと隣接行列の比較

表現方法を選択する前に、実際のグラフワークロードで支配的な操作に関して、両方のモデルを並べて比較することが役立ちます。

Opera生産隣接行列隣接リスト
空間複雑性O(V²)O(V + E)
頂点を追加するO(V²)O(1)
エッジを加えるO(1)O(1)
端を取り除くO(1)O(エ)
エッジ(i, j)が存在するかどうかを確認します。O(1)O(iの次数)
i の近傍を反復処理するO(V)O(iの次数)
ベスト密なグラフ、頻繁なエッジクエリ疎なグラフ、走査負荷の高いタスク

要するに、隣接行列は定数時間でのエッジ検索に優れており、隣接リストはメモリと隣接ノードの反復処理に優れているため、BFS、DFS、ダイクストラ法などのアルゴリズムは通常、隣接リストと組み合わせて使用​​される。

グラフ表現の利点と欠点

それぞれの表現方法には、独自のトレードオフが存在します。両モデルの長所と短所を理解することで、解決しようとしている問題に最適なモデルを選択することができます。

隣接行列の利点:

  • 任意の2つの頂点間の辺の存在確認クエリは定数時間O(1)で実行できます。
  • 固定インデックスを用いることで、フロイド・ウォーシャル法や推移閉包法といった行列ベースのアルゴリズムを容易に実装できる。
  • 重み付けされたエッジは、単一のマトリックスセル内に自然に収まります。

隣接行列の欠点:

  • グラフが疎な場合、O(V²)のメモリを無駄に消費する。
  • 新しい頂点を追加するには、行列全体のサイズを変更する必要があります。
  • 単一の頂点の隣接頂点を反復処理するには、その頂点が少数のエッジしか持たない場合でも、O(V) の時間がかかります。

隣接リストの利点:

  • 使用するメモリ量はO(V + E)のみで、これは疎グラフにおける実際のエッジ数に近い値です。
  • 新しい頂点や辺を追加するのはO(1)です。
  • BFSやDFSなどの探索アルゴリズムは、O(次数)で隣接ノードを反復処理するため、全体の実行時間はO(V + E)となります。

隣接リストの欠点:

  • 特定のエッジが存在するかどうかを確認するのにかかる時間は、O(1)ではなくO(次数)になります。
  • リンクリストはメモリ全体に分散しているため、キャッシュの局所性は弱くなります。
  • 重み付きエッジには、対応するフィールドまたはペアのリストが必要となり、データ構造がやや複雑になります。

隣接リストと隣接行列の使い分け

表現方法の選択は、グラフの密度と最も頻繁に実行する操作によって異なります。適切な構造を選択するには、以下のクイックガイドを参照してください。

  • 隣接行列を優先する グラフが密である場合(E が V² に近い場合)、エッジがほとんど変化しない場合、およびアルゴリズムが「i と j の間にエッジはありますか?」と何度も尋ねる場合。
  • 隣接リストを優先する グラフが疎である場合 (E が V² よりはるかに小さい場合)、実行中に頂点またはエッジのセットが増加する場合、BFS、DFS、または ダイクストラの最短経路アルゴリズム.
  • 混合モデルを好む (隣接リストとエッジのハッシュセット)高速な隣接反復と O(1) のエッジクエリの両方が必要な場合、追加のメモリを犠牲にして使用します。

NetworkXやigraphといった最新のグラフライブラリは、隣接リストをデフォルトとして使用します。これは、ソーシャルネットワーク、道路地図、ウェブページ、パッケージの依存関係など、現実世界のグラフのほとんどが疎で、走査処理が多いためです。

よくあるご質問

隣接リストは、V個の連結リストの配列であり、インデックスiの各リストには、頂点iに隣接するすべての頂点が格納されます。メモリ使用量はO(V + E)であり、疎グラフや、BFSやDFSなどの探索アルゴリズムに適しています。

隣接行列は V × V の 2 次元配列であり、行列 [i][j] にはエッジの重みが格納され、頂点 i と頂点 j の間にエッジが存在する場合は 1 が格納されます。エッジの検索は O(1) ですが、メモリは常に O(V²) です。

隣接行列は、エッジの存在クエリにO(1)で応答します。隣接リストは、隣接ノードをO(次数)で反復処理するため、BFS、DFS、ダイクストラ法などの探索アルゴリズムでは高速です。どちらが最適かは、ワークロードの大部分を占める操作によって異なります。

グラフが疎である場合、実行中に頂点や辺が変化する場合、アルゴリズムが頻繁に隣接ノードを走査する場合に、隣接リストを使用します。ソーシャルネットワーク、道路地図、ウェブページのグラフはすべてこの条件に当てはまります。

グラフが密である場合、頂点集合が固定されている場合、およびアルゴリズムが同じエッジを繰り返し照会する場合は、隣接行列を使用します。フロイド・ウォーシャル法と推移閉包法はどちらも隣接行列上で自然に機能します。

はい。有向グラフの場合、行列は対称ではなく、リストには出力隣接ノードのみが格納されます。重み付きグラフの場合、行列のセルには重みが格納され、リストには隣接ノードと重みのペアが格納されます。

グラフニューラルネットワークは、不正検出、分子特性予測、および推薦システムのために、隣接行列または疎なエッジテンソルを機械学習層に入力します。知識グラフは、検索機能を強化したAIのために、隣接リストエンコーディングにも依存しています。

はい。GitHub CopilotとChatGPTは隣接リストとマトリックスの定型文を生成します。 Python, C++, Java開発者は、重複するエッジ、自己ループ、有向グラフや重み付きグラフの正しい処理など、エッジケースを検証する必要があります。