データ構造とアルゴリズム: グラフ構造

データ構造とアルゴリズム: グラフ構造

写真

グラフ構造は、ツリー構造よりも複雑な非線形構造です。ツリー構造では、ノード間に分岐階層関係があります。各層のノードは、前の層のノードのうち最大 1 つにのみ関連付けることができますが、次の層のノードは複数に関連付けることができます。グラフ構造では、任意の 2 つのノードが関連付けられる可能性があり、つまり、ノード間の隣接関係は任意になります。

したがって、グラフ構造はさまざまな複雑なデータ オブジェクトを記述するために使用され、自然科学、社会科学、人文科学などの多くの分野で非常に幅広く応用されています。グラフ構造は、コンピュータサイエンス、人工知能、電子回路解析、最短経路探索、エンジニアリング計画、化合物分析、統計力学、遺伝学、サイバネティクス、言語学、社会科学など、さまざまな分野で使用されています。グラフ構造は、あらゆるデータ構造の中で最も広く使用されていると言えます。地下鉄駅の路線図など:

グラフの定義

グラフは、ノードが 0 個以上の隣接要素を持つことができるデータ構造です。2 つのノード間の接続はエッジと呼ばれます。グラフ構造内のノードは頂点とも呼ばれ、1 つの頂点から別の頂点までの線はパスと呼ばれます。

  • グラフ構造には、無向グラフ、有向グラフ、重み付きグラフの 3 種類があります。
  • 無向グラフ: 頂点 A と頂点 B の間の辺は無向であり、A から B へ、または B から A へ向かうことができます。
  • 有向グラフ: 頂点 A と頂点 B の間の辺は有向であり、A から B へは行けるが、B から A へは行けない。
  • 重み付きグラフ: 頂点 A と頂点 B 間のエッジには、A から B までの距離などの属性があります。

グラフの表現方法

グラフを表現する方法は2つあります。隣接行列(2次元配列を使用)と隣接リスト(配列+リンクリストを使用)です。

隣接行列

隣接行列はグラフ内の頂点間の関係を表します。行列の行と列は頂点に対応し、座標位置の値はそれらの間の関係に対応し、接続の場合は 1、接続なしの場合は 0 になります。プログラムでは2次元配列を使用して実装されます。

隣接リスト

隣接リストは存在するエッジのみを対象とし、存在しないエッジにスペースを割り当てる必要はありません。したがって、隣接行列と比較して、不必要なスペースの浪費を回避できます。プログラムでは、配列 + リンクリストの形式で実装されています。配列には対応する頂点が格納され、リンクリストには頂点に接続されたすべての頂点が格納されます。

グラフ検索アルゴリズム

グラフィックス構造の基本的なプロパティとメソッド

以下のコードデモンストレーションはすべて隣接行列式の形式で実装されています。

  1. //グラフ構造(隣接行列)
  2. クラスグラフ{
  3. //グラフ内のすべての頂点を保存する
  4. プライベートList<String>頂点;
  5. //グラフ構造の隣接行列
  6. プライベートint [][] 行列;
  7. //各頂点の訪問ステータス。trueは訪問済み、 falseは訪問されていないことを意味します。
  8. プライベートブール値[] 訪問済み;
  9.  
  10. /**
  11. * 渡された頂点情報に基づいて行列を生成する
  12. * @param s
  13. */
  14. パブリックグラフ(文字列s[]) {
  15. 頂点 = 新しいArrayList<>();
  16. (文字列頂点: s){
  17. vertexes.add (頂点) ;
  18. }
  19. 行列 = 新しいint [s.length][s.length];
  20. }
  21.  
  22. /**
  23. * 2つの頂点を接続してエッジを生成します
  24. * @param index1 コレクション内の頂点のインデックス
  25. * @param インデックス2
  26. */
  27. パブリックvoid connect ( intインデックス1、 intインデックス2) {
  28. if (index1 < 0 || index1 > 行列の長さ || index2 < 0 || index2 > 行列の長さ){
  29. 新しい RuntimeException( "頂点が存在しません" ) をスローします。
  30. }
  31. //新しい隣接性を隣接行列に追加する
  32. 行列[インデックス1][インデックス2] = 1;
  33. 行列[インデックス2][インデックス1] = 1;
  34. }
  35.  
  36. /**
  37. * 隣接行列を表示する
  38. */
  39. パブリックvoid showGraphMatrix(){
  40. ( int arr[] : 行列) {
  41. System.out.println (Arrays.toString(arr)) ;
  42. }
  43. }
  44.      
  45. /**
  46. * 隣接行列の対応する行の最初の隣接頂点の添え字を取得します。
  47. * @param 行
  48. * @return隣接する頂点がある場合はその頂点のインデックスを返し、そうでない場合は -1 を返します。
  49. */
  50. 公共  int getFirstNeighbor( int row){
  51. ( int i =0; i<matrix.length; i++) {
  52. (行列[行][i] != 0)の場合{
  53. iを返します
  54. }
  55. }
  56. -1 を返します
  57. }
  58.  
  59. /**
  60. * 隣接行列の列colの行の頂点の次の隣接頂点を取得します。
  61. * @param 行
  62. * @param 列
  63. * @return隣接する頂点がある場合はその頂点のインデックスを返し、そうでない場合は -1 を返します。
  64. */
  65. 公共  int getNeighbor( int行, int列){
  66. ( int i=col+1; i<matrix.length; i++) {
  67. (行列[行][i] != 0)の場合{
  68. iを返します
  69. }
  70. }
  71. -1 を返します
  72. }
  73. }

深さ優先探索

深さ優先探索はグラフ アルゴリズムの一種で、DFS (Depth First Search) と略されます。簡単に言うと、このプロセスは、それ以上深く探索できなくなるまですべての可能な分岐パスを探索し、各ノードには 1 回しかアクセスできないというものです。このようなアクセス戦略では、頂点のすべての隣接頂点への水平アクセスではなく、垂直掘削が優先されます。簡単に言えば、最後まで道を進み、うまくいかなかったら引き返すことを意味します。

アイデア: 現在の頂点に接続されているがまだ訪問されていない頂点を選択し、現在のノードを隣接する頂点に移動します。訪問されていない隣接する頂点がない場合は、前の頂点の位置に戻ってこの手順を続行します。すべての頂点が訪問されるまで。

隣接しているがまだ訪問していない頂点に移動する

未訪問の隣接頂点は存在しないため、未訪問の隣接頂点に遭遇するまで戻ります。

すべての頂点を訪問したら、アルゴリズムを終了します。

以下は深さ優先探索プロセスのアニメーションです。

コードデモンストレーション

  1. パブリックvoid dsf(){
  2. 訪問 = 新しいブール値[ vertexes.size ()];
  3. // セット内のインデックス 0 の頂点を使用して深さ検索を実行します
  4. dsf(訪問数, 0);
  5. }
  6.  
  7. /**
  8. * 深さ優先探索
  9. * @param 訪問済み
  10. * @param 行
  11. */
  12. パブリックvoid dsf(boolean[] 訪問済み、 int行){
  13. // 現在の頂点を出力する
  14. システム.out.print (vertexes.get(row) + " -> " );
  15. //現在の頂点を訪問済みに設定する
  16. 訪問[行] = true ;
  17. //現在の頂点の隣接頂点の添え字を取得します
  18. 整数 インデックス= getFirstNeighbor(行);
  19. //現在の頂点に隣接する頂点がある場合は、深度検索を実行します
  20. while (インデックス!= -1){
  21. //隣接する頂点が訪問されていない場合は、再帰的に走査します
  22. if (visited[インデックス] != true ){
  23. dsf(訪問、インデックス);
  24. }
  25. //隣接する頂点が訪問されたら、別の隣接する頂点を見つける
  26. インデックス= getNeighbor(行、インデックス);
  27. }
  28. }

幅優先探索

幅優先探索アルゴリズム (幅優先探索とも呼ばれる) は、最も単純なグラフ検索アルゴリズムの 1 つであり、多くの重要なグラフ アルゴリズムのプロトタイプでもあります。ダイクストラの単一ソース最短経路アルゴリズムとプリムの最小全域木アルゴリズムはどちらも幅優先探索に似た考え方を採用しています。別名は BFS とも呼ばれ、グラフ内のすべてのノードを体系的に拡張してチェックし、結果を見つけることを目的としたブラインド検索方法です。つまり、結果の可能性のある場所を考慮せず、結果が見つかるまでグラフ全体を徹底的に検索します。

幅優先探索アルゴリズムは、階層的探索プロセスに似ています。幅優先探索アルゴリズムでは、訪問した頂点の順序を維持するためのキューが必要であり、これにより、これらの頂点の隣接頂点をこの順序で訪問できるようになります。

アイデア: 現在の頂点の隣接頂点を順番に訪問し、訪問順にこれらの隣接頂点をキューに格納します。現在の頂点のすべての隣接頂点を訪問したら、キューから頂点をポップし、すべての頂点を訪問するまで、その頂点を現在の頂点としてこの手順を続けます。

現在の頂点の隣接する頂点を順に訪問し、訪問順にこれらの隣接する頂点をキューに格納します。

現在の頂点には、未訪問の隣接頂点がありません。キューから頂点をポップし、ポップした頂点を使用して、未訪問の隣接頂点の訪問を続行します。

グラフ内のすべての頂点が訪問済みであっても、アルゴリズムはキュー内のすべての頂点がポップアウトされて訪問されるまで待機してから終了する必要があることに注意してください。

以下は幅優先探索プロセスのアニメーションです。

コードデモンストレーション

  1. パブリックボイドbfs(){
  2. 訪問 = 新しいブール値[ vertexes.size ()];
  3. ////セット内のインデックス0の頂点に対して幅優先探索を実行します
  4. bfs(訪問数, 0);
  5. }
  6.  
  7. /**
  8. * 幅優先探索
  9. * @param 訪問済み
  10. * @param 行
  11. */
  12. パブリックvoid bfs(boolean[] 訪問済み、 int行){
  13. //隣接する頂点を走査する順序を格納するキューを作成する
  14. LinkedList キュー = new LinkedList();
  15. // 現在の頂点を出力する
  16. システム.out.print (vertexes.get(row) + " -> " );
  17. //現在の頂点を訪問済みに設定する
  18. 訪問[行] = true ;
  19. //現在の頂点をキューに追加します
  20. キューに行を追加します
  21. //キューが空でない場合、つまり未検索の隣接頂点がある場合、検索
  22. while (!queue.isEmpty()){
  23. // キューから隣接する頂点インデックスを順番にポップアップします
  24. 整数 最後= (整数)queue.removeFirst();
  25. //ポップアップ頂点の隣接頂点の添え字を取得します
  26. 整数 インデックス= getFirstNeighbor(最後);
  27. //ポップアップ頂点に隣接する頂点がある場合は、幅検索を実行します
  28. while(インデックス!=-1){
  29. //隣接する頂点が訪問されていない場合
  30. if(visited[インデックス]!= true ){
  31. // 隣接する頂点を出力する
  32. システム.out.print (vertexes.get( index )+ "->" );
  33. //隣接する頂点を訪問済みに設定する
  34. 訪問[インデックス] = true ;
  35. //隣接する頂点をキューに追加します
  36. キューに最後にインデックスを追加します
  37. }
  38. //ポップアップ頂点の別の隣接頂点を探し続けます
  39. インデックス= getNeighbor(最後インデックス);
  40. }
  41. }
  42. }

完全なデモコード

  1. パブリッククラスGraphDemo {
  2. 公共 静的void main(String[] args) {
  3. 文字列[] s = { "A" "B" "C" "D" "E" "F" "G" };
  4. グラフ graph = 新しいグラフ;
  5. //AB AC AG AF FD FE DE EG
  6. グラフに接続します(0, 1);
  7. グラフに接続します(0, 2);
  8. グラフに接続します(0, 6);
  9. グラフに接続します(0, 5);
  10. グラフに接続します(5, 3);
  11. グラフに接続します(5, 4);
  12. グラフに接続します(3, 4);
  13. グラフ.connect (4, 6);
  14. グラフマトリックスを表示します。
  15.  
  16. graph.dsf(); //A -> B -> C -> F -> D -> E -> G ->
  17. System.out.println( ) ;
  18. graph.bfs(); //A -> B -> C -> F -> G -> D -> E ->
  19. }
  20. }
  21.  
  22. //グラフィック構造
  23. クラスグラフ{
  24. //グラフ内のすべての頂点を保存する
  25. プライベートList<String>頂点;
  26. //グラフ構造の隣接行列
  27. プライベートint [][] 行列;
  28. //各頂点の訪問ステータス。trueは訪問済み、 falseは訪問されていないことを意味します。
  29. プライベートブール値[] 訪問済み;
  30.  
  31. /**
  32. * 渡された頂点情報に基づいて行列を生成する
  33. * @param s
  34. */
  35. パブリックグラフ(文字列s[]) {
  36. 頂点 = 新しいArrayList<>();
  37. (文字列頂点: s){
  38. vertexes.add (頂点) ;
  39. }
  40. 行列 = 新しいint [s.length][s.length];
  41. }
  42.  
  43. /**
  44. * 2つの頂点を接続してエッジを生成します
  45. * @param index1 コレクション内の頂点のインデックス
  46. * @param インデックス2
  47. */
  48. パブリックvoid connect ( intインデックス1、 intインデックス2) {
  49. if (index1 < 0 || index1 > 行列の長さ || index2 < 0 || index2 > 行列の長さ){
  50. 新しい RuntimeException( "頂点が存在しません" ) をスローします。
  51. }
  52. //新しい隣接性を隣接行列に追加する
  53. 行列[インデックス1][インデックス2] = 1;
  54. 行列[インデックス2][インデックス1] = 1;
  55. }
  56.  
  57. /**
  58. * 隣接行列を表示する
  59. */
  60. パブリックvoid showGraphMatrix(){
  61. ( int arr[] : 行列) {
  62. System.out.println (Arrays.toString(arr)) ;
  63. }
  64. }
  65.  
  66. パブリックvoid dsf(){
  67. 訪問 = 新しいブール値[ vertexes.size ()];
  68. // セット内のインデックス 0 の頂点に対して深さ優先探索を実行します
  69. dsf(訪問数, 0);
  70. }
  71.  
  72. /**
  73. * 深さ優先探索
  74. * @param 訪問済み
  75. * @param 行
  76. */
  77. パブリックvoid dsf(boolean[] 訪問済み、 int行){
  78. // 現在の頂点を出力する
  79. システム.out.print (vertexes.get(row) + " -> " );
  80. //現在の頂点を訪問済みに設定する
  81. 訪問[行] = true ;
  82. //現在の頂点の隣接頂点の添え字を取得します
  83. 整数 インデックス= getFirstNeighbor(行);
  84. //現在の頂点に隣接する頂点がある場合は、深度検索を実行します
  85. while (インデックス!= -1){
  86. //隣接する頂点が訪問されていない場合は、再帰的に走査します
  87. if (visited[インデックス] != true ){
  88. dsf(訪問、インデックス);
  89. }
  90. //隣接する頂点が訪問されたら、別の隣接する頂点を見つける
  91. インデックス= getNeighbor(行、インデックス);
  92. }
  93. }
  94.  
  95. パブリックボイドbfs(){
  96. 訪問 = 新しいブール値[ vertexes.size ()];
  97. ////セット内のインデックス0の頂点に対して幅優先探索を実行します
  98. bfs(訪問数, 0);
  99. }
  100.  
  101. /**
  102. * 幅優先探索
  103. * @param 訪問済み
  104. * @param 行
  105. */
  106. パブリックvoid bfs(boolean[] 訪問済み、 int行){
  107. //隣接する頂点を走査する順序を格納するキューを作成する
  108. キュー queue = new ArrayDeque();
  109. // 現在の頂点を出力する
  110. システム.out.print (vertexes.get(row) + " -> " );
  111. //現在の頂点を訪問済みに設定する
  112. 訪問[行] = true ;
  113. //現在の頂点をキューに追加します
  114. キューに行を追加します
  115. //キューが空でない場合、つまり未検索の隣接頂点がある場合、検索
  116. while (!queue.isEmpty()){
  117. // キューから隣接する頂点インデックスを順番にポップアップします
  118. 整数  last = (整数)queue.poll();
  119. //ポップアップ頂点の隣接頂点の添え字を取得します
  120. 整数 インデックス= getFirstNeighbor(最後);
  121. //ポップアップ頂点に隣接する頂点がある場合は、幅検索を実行します
  122. while(インデックス!=-1){
  123. //隣接する頂点が訪問されていない場合
  124. if(visited[インデックス]!= true ){
  125. // 隣接する頂点を出力する
  126. システム.out.print (vertexes.get( index )+ "->" );
  127. //隣接する頂点を訪問済みに設定する
  128. 訪問[インデックス] = true ;
  129. //隣接する頂点をキューに追加します
  130. キューに追加(インデックス)
  131. }
  132. //ポップアップ頂点の別の隣接頂点を探し続けます
  133. インデックス= getNeighbor(最後インデックス);
  134. }
  135. }
  136. }
  137.  
  138. /**
  139. * 隣接行列の対応する行の最初の隣接頂点の添え字を取得します。
  140. * @param 行
  141. * @return隣接する頂点がある場合はその頂点のインデックスを返し、そうでない場合は -1 を返します。
  142. */
  143. 公共  int getFirstNeighbor( int row){
  144. ( int i =0; i<matrix.length; i++) {
  145. (行列[行][i] != 0)の場合{
  146. iを返します
  147. }
  148. }
  149. -1 を返します
  150. }
  151.  
  152. /**
  153. * 隣接行列の列colの行の頂点の次の隣接頂点を取得します。
  154. * @param 行
  155. * @param 列
  156. * @return隣接する頂点がある場合はその頂点のインデックスを返し、そうでない場合は -1 を返します。
  157. */
  158. 公共  int getNeighbor( int行, int列){
  159. ( int i=col+1; i<matrix.length; i++) {
  160. (行列[行][i] != 0)の場合{
  161. iを返します
  162. }
  163. }
  164. -1 を返します
  165. }
  166. }

<<:  マイルストーンではありません! Facebookの100言語翻訳モデルは過大評価され、疑問視されている

>>:  ディープマインドはニューラルネットワークを使ってシュレーディンガー方程式を解く物理学論文を発表した。

ブログ    
ブログ    
ブログ    

推薦する

初心者のための CNN と Keras のクイックガイド

[[201203]] 1. Keras を使用する理由ディープラーニングが大人気の昨今、サードパーテ...

「手抜きアルゴリズム」は大企業をターゲットにしており、これがそれだ

[[342088]]基本的なデータ構造の統合は、大規模システムの基礎となります。たとえば、Redis...

機械学習モデルの仕組み

この記事は公開アカウント「Reading Core Technique」(ID: AI_Discov...

大規模ウェブサイトのアルゴリズムとアーキテクチャに関する簡単な説明

順序先月、上司が「大規模ウェブサイトのアルゴリズムとアーキテクチャに関する簡単な説明」という講義をし...

IoTが災害管理にどのように役立つか

[[405572]]災害管理における IoT の活用は、災害を予測し、早期に当局に警告し、災害の影響...

FP8 を使用して大規模モデルをトレーニングするとどれくらい良いのでしょうか? Microsoft: BF16 より 64% 高速、メモリは 42% 削減

大規模言語モデル (LLM) には、これまでにない言語理解および生成機能が備わっていますが、これらの...

...

インタビュアー: アルゴリズムの時間計算量と空間計算量についてどう思いますか?計算方法は?

[[424483]] 1. はじめにアルゴリズムとは、データを操作し、プログラムの問題を解決するた...

...

AppleがAI研究成果を公開、マルチモーダルLLMモデルFerretをリリース

IT Homeは12月25日、Appleがコロンビア大学の研究者らと協力して2023年10月にオープ...

...

GPSを使用しない自動運転システムソリューション

自動運転技術の発展に伴い、未知の環境におけるスマートカーの測位技術がこの分野の研究の中核となっていま...

Hadoop、Spark、Hive とはいったい何でしょうか? アルゴリズムを開発するには、これらを学ぶ必要がありますか?

[[422888]]みなさんこんにちは。私は梁唐です。最近、多くの新人がアルゴリズム エンジニアに...

...

金融ロボアドバイザーは3つのトレンドによって増加傾向にある

編集者注: ロボット アドバイザーの登場により、従来のアドバイザーはどこへ向かうのでしょうか。これは...