Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
+33 -33
View File
@@ -1,6 +1,6 @@
# グラフ
<u>グラフ</u>は非線形データ構造の一種で、<u>頂点</u>と<u>辺</u>構成されす。グラフ$G$は、頂点集合$V$と辺集合$E$の組み合わせとして抽象的に表現できます。以下の例は、5の頂点と7つの辺を含むグラフを示しています。
<u>グラフgraph</u>は、<u>頂点(vertex</u>と<u>辺edge</u>から構成される非線形データ構造です。グラフ $G$ は、頂点集合 $V$ と辺集合 $E$ からなる集合として抽象的に表ます。以下の例は、5の頂点と 7 本の辺を含むグラフを示しています。
$$
\begin{aligned}
@@ -10,74 +10,74 @@ G & = \{ V, E \} \newline
\end{aligned}
$$
頂点をノード、辺をノードを接続する参照(ポインタ)と見なすと、グラフは連結リストから拡張されたデータ構造として見ることができます。図に示すように、**線形関係(連結リスト)や分割統治関係(木)と比較して、ネットワーク関係(グラフ)は自由度が高いため、より複雑です**
頂点をノード、辺をノードをつなぐ参照(ポインタ)とみなせば、グラフは連結リストを拡張したデータ構造の一種と捉えられます。次の図に示すように、**線形関係(連結リスト)や分治関係(木)と比て、ネットワーク関係(グラフ)は自由度が高く**、そのぶん複雑です。
![連結リスト、木、グラフの関係](graph.assets/linkedlist_tree_graph.png)
## グラフの一般的な種類と用語
グラフは、辺に方向があるかどうかによって<u>無向グラフ</u>と<u>有向グラフ</u>に分けることができます(下図参照)
辺が方向性を持つかどうかに応じて、<u>無向グラフundirected graph</u>と<u>有向グラフ(directed graph</u>に分けられます。次の図のとおりです
- 無向グラフでは、辺は2つの頂点間の「双方向」接続を表します。例えば、Facebookの「友達」関係です。
- 有向グラフでは、辺方向性があります。つまり、辺$A \rightarrow B$$A \leftarrow B$は互いに独立しています。例えば、InstagramやTikTokの「フォロー」と「フォロワー」の関係です。
- 無向グラフでは、辺は 2 つの頂点間の「双方向」接続関係を表します。例えば WeChat や QQ における「友だち関係です。
- 有向グラフでは、辺方向性を持ち、すなわち $A \rightarrow B$$A \leftarrow B$ の 2 方向の辺は互いに独立す。例えば Weibo や Douyin における「フォロー」と「フォロワー」の関係です。
![有向グラフと無向グラフ](graph.assets/directed_graph.png)
すべての頂点が接続されているかどうかによって、グラフは<u>連結グラフ</u>と<u>非連結グラフ</u>に分けることができます(下図参照)
すべての頂点が連結しているかどうかに応じて、<u>連結グラフ(connected graph</u>と<u>非連結グラフ(disconnected graph</u>に分けられます。次の図のとおりです
- 連結グラフでは、任意の頂点から開始して他の任意の頂点に到達することが可能です。
- 非連結グラフでは、任意の開始頂点から到達できない頂点が少なくとも1つ存在します
- 連結グラフでは、ある頂点から出発すると、ほかの任意の頂点に到達できます。
- 非連結グラフでは、ある頂点から出発すると、少なくとも 1 つの頂点には到達できません
![連結グラフと非連結グラフ](graph.assets/connected_graph.png)
辺に重み変数を追加することもでき、その結果として<u>重み付きグラフ</u>が生まれます(下図参照)。例えば、Instagramでは、システムがあなたと他のユーザーとの間の相互作用レベル(いいね、閲覧、コメントなど)によってフォロワーとフォロー中のリストをソートします。このような相互作用ネットワークは重み付きグラフで表現できます。
辺に重み」の変数を追加すると、次の図に示すような<u>重み付きグラフ(weighted graph</u>が得られます。例えば『Honor of Kings』のようなモバイルゲームでは、システムが共にプレイした時間に基づいてプレイヤー間の「親密度」を計算します。この親密度ネットワークは重み付きグラフで表ます。
![重み付きグラフと重みなしグラフ](graph.assets/weighted_graph.png)
グラフデータ構造には、以下のような一般的に使用される用語があります。
グラフというデータ構造には、のような基本用語があります。
- <u>隣接</u>2つの頂点を接続する辺がある場合、これら2つの頂点は「隣接していると言われます。上図では、頂点1の隣接頂点は頂点2、3、5です。
- <u>パス</u>:頂点Aから頂点Bまでに通過する辺のシーケンスを、AからBへのパスと呼びます。上図では、辺のシーケンス1-5-2-4は頂点1から頂点4へのパスです。
- <u>次数</u>:頂点が持つ辺の数です。有向グラフの場合、<u>入次数</u>はその頂点を指す辺の数、<u>出次数</u>はその頂点から出る辺の数をします。
- <u>隣接adjacency</u>2 つの頂点の間に辺が存在するとき、この 2 つの頂点は「隣接している」といいます。上図では、頂点 1 に隣接する頂点は 2、3、5 です。
- <u>経路(path</u>:頂点 A から頂点 B までに通過する辺で構成された列を、A から B への「経路」と呼びます。上図では、辺の1-5-2-4 は頂点 1 から頂点 4 への 1 本の経路です。
- <u>次数degree</u>ある頂点が持つ辺の数です。有向グラフでは、<u>入次数in-degree</u>はその頂点に向かう辺の本数を表し、<u>出次数(out-degree</u>はその頂点から出る辺の数をします。
## グラフの表現
グラフの一般的な表現には「隣接行列」と「隣接リスト」があります。以下の例では無向グラフを使用します。
グラフの一般的な表現方法には「隣接行列」と「隣接リスト」があります。以下では無向グラフを例に説明します。
### 隣接行列
グラフの頂点数を$n$とすると、<u>隣接行列</u>は$n \times n$の行列を使用してグラフを表します。各行(列)は頂点を表し、行列要素は辺を表し、2つの頂点間に辺があるかどうかを$1$または$0$で示します。
グラフの頂点数を $n$ とすると、<u>隣接行列adjacency matrix</u>は $n \times n$ の行列を用いてグラフを表します。各行(列)は 1 つの頂点を表し、行列要素は辺を表します。$1$ または $0$ を用いて、2 つの頂点間に辺があるかどうかを示します。
下図に示すように、隣接行列を$M$、頂点リストを$V$とすると、行列要素$M[i, j] = 1$は頂点$V[i]$頂点$V[j]$の間に辺があることをし、逆に$M[i, j] = 0$は2つの頂点間に辺がないことをします。
次の図のように、隣接行列を $M$、頂点リストを $V$ とすると、行列要素 $M[i, j] = 1$ は頂点 $V[i]$ から頂点 $V[j]$ への辺が存在することをし、逆に $M[i, j] = 0$ は 2 つの頂点間に辺がないことをします。
![隣接行列によるグラフの表現](graph.assets/adjacency_matrix.png)
![グラフの隣接行列による表現](graph.assets/adjacency_matrix.png)
隣接行列には以下の特があります。
隣接行列にはの特があります。
- 頂点は自分自身に接続することはできないため、隣接行列の主対角線上の要素は意味がありません。
- 無向グラフの場合、両方向の辺は等価であるため、隣接行列は主対角線に関して対称です。
- 隣接行列の要素を$1$$0$から重みに置き換えることで、重み付きグラフを表現できます。
- 単純グラフでは、頂点は自分自身とは接続できないため、このとき隣接行列の主対角線上の要素は意味がありません。
- 無向グラフでは、2 方向の辺は等価であるため、このとき隣接行列は主対角線に関して対称です。
- 隣接行列の要素を $1$$0$ から重みに置き換える、重み付きグラフを表ます。
隣接行列でグラフを表現する場合、行列要素に直接アクセスして辺を取得できるため、追加削除検索、変更の操作効率的で、すべて時間計算量$O(1)$です。ただし、行列の空間計算量は$O(n^2)$で、より多くのメモリを消費します。
隣接行列でグラフを表場合、行列要素に直接アクセスして辺を取得できるため、追加削除検索・更新の操作効率は高く、時間計算量はいずれも $O(1)$ です。しかし、行列の空間計算量は $O(n^2)$ であり、メモリ使用量は多くなります。
### 隣接リスト
<u>隣接リスト</u>は$n$の連結リストを使用してグラフを表現し、各連結リストノードは頂点を表します。$i$番目の連結リストは頂点$i$に対応し、すべての隣接頂点(その頂点接続された頂点)を含みます。図は隣接リストを使用して格納されたグラフの例を示しています。
<u>隣接リストadjacency list</u>は$n$の連結リストを使てグラフを表します。連結リストノードは頂点を表します。$i$の連結リストは頂点 $i$ に対応し、その頂点に隣接するすべての頂点(その頂点接続された頂点)を格納します。次の図は隣接リストで保存したグラフの例です。
![隣接リストによるグラフの表現](graph.assets/adjacency_list.png)
![グラフの隣接リストによる表現](graph.assets/adjacency_list.png)
隣接リストは実際の辺のみを格納し、辺の総数は$n^2$よりはるかに少ないことが多く、より空間効率的です。ただし、隣接リストで辺を見つけるに連結リストを走査する必要があるため、その時間効率は隣接行列ほど良くありません。
隣接リストは実際に存在する辺だけを格納し、辺の総数は通常 $n^2$ よりはるかに小さいため、より省スペースです。しかし、隣接リストで辺を見つけるために連結リストを走査する必要があるため、時間効率は隣接行列に及びません。
上図を観察すると、**隣接リストの構造はハッシュテーブルの「チェイン法」と非常に似ているため、同様の方法を使用して効率を最適化できます**。例えば、連結リストが長い場合、それをAVL木や赤黒木に変換して時間効率を$O(n)$から$O(\log n)$に最適化できます。連結リストをハッシュテーブルに変換することで、時間計算量を$O(1)$に削減することもできます。
上図をると、**隣接リストの構造はハッシュテーブルにおける「連鎖アドレス法」と非常によく似ているため、同様の方法効率を最適化できます**。例えば、連結リストが長い場合AVL 木や赤黒木に変換して時間効率を $O(n)$ から $O(\log n)$ に改善できます。さらに、連結リストをハッシュテーブルに変換すれば、時間計算量を $O(1)$ まで下げられます。
## グラフの一般的な応用
下表に示すように、多くの現実世界のシステムはグラフでモデル化でき、対応する問題グラフ計算問題に削減できます。
次の表のように、多くの現実のシステムはグラフでモデル化でき、対応する問題グラフ計算問題に帰着できます。
<p align="center"> 表 <id> &nbsp; 現実生活の一般的なグラフ </p>
<p align="center"> 表 <id> &nbsp; 現実世界でよく見られるグラフ </p>
| | 頂点 | 辺 | グラフ計算問題 |
| -------------- | -------------- | -------------------------------- | --------------------------- |
| ソーシャルネットワーク | ユーザー | フォロー / フォロワー関係 | 潜在的フォロー推薦 |
| 地下鉄路線 | 駅 | 駅間の接続性 | 最短ルート推薦 |
| 太陽系 | 天体 | 天体間の重力 | 惑星軌道計算 |
| | 頂点 | 辺 | グラフ計算問題 |
| -------- | ---- | -------------------- | ------------ |
| ソーシャルネットワーク | ユーザー | 友だち関係 | 潜在的な友だちの推薦 |
| 地下鉄路線 | 駅 | 駅間の接続性 | 最短経路の推薦 |
| 太陽系 | 天体 | 天体間の万有引力作用 | 惑星軌道計算 |
+29 -29
View File
@@ -1,18 +1,18 @@
# グラフの基本操作
グラフの基本操作は「辺」に対する操作と「頂点」に対する操作に分けることができます。「隣接行列」と「隣接リスト」の2つの表現方法の下では、実装が異なります。
グラフの基本操作は「辺」に対する操作と「頂点」に対する操作に分けられます。「隣接行列」と「隣接リスト」の 2 つの表現方法では、実装方法が異なります。
## 隣接行列に基づく実装
$n$個の頂点を持つ無向グラフ与えられた場合、さまざまな操作は下図のように実装されます。
頂点数が $n$ の無向グラフ与えると、各種操作の実装方法は次図のとおりです。
- **辺の追加または削除**:隣接行列内の指定された辺を直接変更、$O(1)$時間を使用します。無向グラフであるため、方向の辺を同時に更新する必要があります。
- **頂点の追加**:隣接行列の末尾に行と列を追加し、すべて$0$で埋めます。$O(n)$時間を使用します。
- **頂点の削除**:隣接行列内の行と列を削除します。最悪の場合は最初の行と列が削除されるときで、$(n-1)^2$個の要素を「上と左に移動」る必要があ、$O(n^2)$時間を使用します。
- **初期化**$n$個の頂点を渡し、長さ$n$の頂点リスト`vertices`を初期化し、$O(n)$時間を使用します。$n \times n$サイズの隣接行列`adjMat`を初期化し、$O(n^2)$時間を使用します。
- **辺の追加または削除**:隣接行列で指定した辺を直接変更すればよく、$O(1)$ 時間す。無向グラフであるため、2 方向の辺を同時に更新する必要があります。
- **頂点の追加**:隣接行列の末尾に 1 行 1 列を追加し、すべて$0$ で埋めればよく、$O(n)$ 時間す。
- **頂点の削除**:隣接行列から 1 行 1 列を削除します。先頭行と先頭列を削除する場合が最悪で、$(n-1)^2$ 個の要素を「左上へ移動」させる必要があるため、$O(n^2)$ 時間す。
- **初期化**$n$ 個の頂点を受け取り、長さ $n$ の頂点リスト `vertices` を初期化するのに $O(n)$ 時間、サイズ $n \times n$ の隣接行列 `adjMat` を初期化するのに $O(n^2)$ 時間かかります。
=== "隣接行列の初期化"
![隣接行列の初期化、辺の追加と削除、頂点の追加と削除](graph_operations.assets/adjacency_matrix_step1_initialization.png)
![隣接行列の初期化、辺の追加と削除、頂点の追加と削除](graph_operations.assets/adjacency_matrix_step1_initialization.png)
=== "辺の追加"
![adjacency_matrix_add_edge](graph_operations.assets/adjacency_matrix_step2_add_edge.png)
@@ -26,7 +26,7 @@ $n$個の頂点を持つ無向グラフが与えられた場合、さまざま
=== "頂点の削除"
![adjacency_matrix_remove_vertex](graph_operations.assets/adjacency_matrix_step5_remove_vertex.png)
以下は隣接行列を使用して表現されたグラフの実装コードです:
以下は隣接行列でグラフを表した実装コードです:
```src
[file]{graph_adjacency_matrix}-[class]{graph_adj_mat}-[func]{}
@@ -34,16 +34,16 @@ $n$個の頂点を持つ無向グラフが与えられた場合、さまざま
## 隣接リストに基づく実装
総計$n$個の頂点と$m$個の辺を持つ無向グラフが与えられた場合、さまざまな操作は図のように実装できます。
無向グラフの頂点総数を $n$、辺総数を $m$ とすると、各種操作は図の方法で実装できます。
- **辺の追加**:対応する頂点の連結リストの末尾に辺を追加するだけで、$O(1)$時間を使用します。無向グラフであるため、両方向に同時に辺を追加する必要があります。
- **辺の削除**:対応する頂点の連結リスト内で指定された辺を見つけて削除し、$O(m)$時間を使用します。無向グラフでは、方向の辺を同時に削除する必要があります。
- **頂点の追加**:隣接リストに連結リストを追加し、新しい頂点をリストのヘッドノードにし、$O(1)$時間を使用します。
- **頂点の削除**:隣接リスト全体を走査し、指定された頂点を含むすべての辺を削除する必要があ、$O(n + m)$時間を使用します。
- **初期化**:隣接リストに$n$個の頂点と$2m$の辺を作成、$O(n + m)$時間を使用します。
- **辺の追加**頂点に対応する連結リストの末尾に辺を追加すればよく、$O(1)$ 時間す。無向グラフなので、2 方向の辺を同時に追加する必要があります。
- **辺の削除**頂点に対応する連結リストから指定した辺を探して削除するため、$O(m)$ 時間す。無向グラフでは、2 方向の辺を同時に削除する必要があります。
- **頂点の追加**:隣接リストに 1 つの連結リストを追加し、新しい頂点をその連結リストの先頭ノードとするため、$O(1)$ 時間す。
- **頂点の削除**:隣接リスト全体を走査し、指定た頂点を含むすべての辺を削除する必要があるため、$O(n + m)$ 時間す。
- **初期化**:隣接リストに $n$ 個の頂点と $2m$の辺を作成するため、$O(n + m)$ 時間す。
=== "隣接リストの初期化"
![隣接リストの初期化、辺の追加と削除、頂点の追加と削除](graph_operations.assets/adjacency_list_step1_initialization.png)
![隣接リストの初期化、辺の追加と削除、頂点の追加と削除](graph_operations.assets/adjacency_list_step1_initialization.png)
=== "辺の追加"
![adjacency_list_add_edge](graph_operations.assets/adjacency_list_step2_add_edge.png)
@@ -57,12 +57,12 @@ $n$個の頂点を持つ無向グラフが与えられた場合、さまざま
=== "頂点の削除"
![adjacency_list_remove_vertex](graph_operations.assets/adjacency_list_step5_remove_vertex.png)
以下は隣接リストのコード実装です。上図と比較して、実際のコードには以下の違いがあります。
以下は隣接リストのコード実装です。上図と比べると、実際のコードにはの違いがあります。
- 頂点の追加と削除の便宜、およびコードの簡素化のため、連結リストの代わりにリスト(動的配列)を使用します。
- ハッシュテーブルを使用して隣接リストを格納し、`key`頂点インスタンス、`value`その頂点隣接頂点のリスト(連結リスト)です。
- 頂点の追加と削除を容易にし、コードを簡潔にするため、連結リストの代わりにリスト(動的配列)を使用しています。
- ハッシュテーブルを用いて隣接リストを格納しており`key`頂点インスタンス、`value`その頂点隣接する頂点のリスト(連結リスト)です。
さらに、隣接リストで頂点を表現するために`Vertex`クラスを使用します。その理由は隣接行列のようにリストインデックスを使用して異なる頂点を区別する場合、インデックス$i$の頂点を削除したい場合、隣接リスト全体を走査し、$i$より大きいすべてのインデックスを1つずつ減少させる必要があり、これは非常に非効率的です。しかし、各頂点が一意`Vertex`インスタンスである場合、頂点を削除しても他の頂点変更を加える必要ありません。
また、隣接リストで頂点を表ために `Vertex` クラスを使用しています。その理由は、もし隣接行列と同様にリストインデックス異なる頂点を区別する、インデックス $i$ の頂点を削除する場合、隣接リスト全体を走査し、$i$ より大きいすべてのインデックスを $1$ 減らす必要があり、効率が非常に低いためです。これに対して、各頂点が一意`Vertex` インスタンスであれば、ある頂点を削除しても他の頂点変更る必要ありません。
```src
[file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{}
@@ -70,17 +70,17 @@ $n$個の頂点を持つ無向グラフが与えられた場合、さまざま
## 効率の比較
グラフに$n$個の頂点と$m$の辺があると仮定すると、表は隣接行列と隣接リストの時間効率空間効率を比較しています。
グラフに $n$ 個の頂点と $m$の辺があるとすると、次の表は隣接行列と隣接リストの時間効率および空間効率を比較したものです。なお、隣接リスト(連結リスト)は本記事の実装に対応し、隣接リスト(ハッシュテーブル)はすべての連結リストをハッシュテーブルに置き換えた実装を指します。
<p align="center"> 表 <id> &nbsp; 隣接行列と隣接リストの比較 </p>
| | 隣接行列 | 隣接リスト(連結リスト) | 隣接リスト(ハッシュテーブル) |
| ---------------- | -------------- | ----------------------- | ----------------------------- |
| 隣接性の判定 | $O(1)$ | $O(m)$ | $O(1)$ |
| 辺の追加 | $O(1)$ | $O(1)$ | $O(1)$ |
| 辺の削除 | $O(1)$ | $O(m)$ | $O(1)$ |
| 頂点の追加 | $O(n)$ | $O(1)$ | $O(1)$ |
| 頂点の削除 | $O(n^2)$ | $O(n + m)$ | $O(n)$ |
| メモリ空間使用量 | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ |
| | 隣接行列 | 隣接リスト(連結リスト) | 隣接リスト(ハッシュテーブル) |
| ------------ | -------- | -------------- | ---------------- |
| 隣接判定 | $O(1)$ | $O(n)$ | $O(1)$ |
| 辺の追加 | $O(1)$ | $O(1)$ | $O(1)$ |
| 辺の削除 | $O(1)$ | $O(n)$ | $O(1)$ |
| 頂点の追加 | $O(n)$ | $O(1)$ | $O(1)$ |
| 頂点の削除 | $O(n^2)$ | $O(n + m)$ | $O(n)$ |
| メモリ使用量 | $O(n^2)$ | $O(n + m)$ | $O(n + m)$ |
上表を観察すると、隣接リスト(ハッシュテーブル)が最高の時間効率と空間効率を持っているように見えます。しかし実際には、隣接行列での辺に対する操作がより効率的で、単一の配列アクセスまたは代入操作のみが必要です。全体的に、隣接行列は「空間時間のトレードオフ」の原則を例示し、隣接リストは「時間空間のトレードオフ」を例示しています。
上表をると、隣接リスト(ハッシュテーブル)の時間効率と空間効率が最も優れているように見えます。しかし実際には、隣接行列のほうが辺の操作効率は高く、必要なのは 1 回の配列アクセスまたは代入だけです。総合的に見ると、隣接行列は「空間時間と引き換えにする」原則を体現し、隣接リストは「時間空間と引き換えにする」原則を体現しています。
+36 -32
View File
@@ -1,33 +1,37 @@
# グラフ走査
# グラフ走査
木は「一対多」の関係を表し、グラフはより高い自由度を持ち、任意の「多対多」の関係を表現できます。したがって、木グラフの特別なケースと見なすことができます。明らかに、**木の走査操作もグラフ走査操作の特別なケースです**。
木は「一対多」の関係を表すのに対し、グラフはより高い自由度を持ち、任意の「多対多」の関係を表現できます。したがって、木グラフの一種の特殊な場合とみなせます。明らかに、**木の走査操作もグラフ走査操作の一種の特殊な場合です**。
グラフと木の両方で、走査操作を実するために探索アルゴリズムの応用が必要です。グラフ走査は2つのタイプに分けることができます:<u>幅優先探索(BFS</u>と<u>深さ優先探索(DFS</u>です。
グラフと木はいずれも、走査操作を実するために探索アルゴリズムを用いる必要があります。グラフ走査方法も、<u>幅優先走査</u>と<u>深さ優先走査</u>の 2 種類に分けられます。
## 幅優先探索
## 幅優先走査
**幅優先探索は近くから遠くへの走査方法で、ある頂点から開始し、常に最も近い頂点を優先的に訪問し、層ごとに外側に展開していきます**図に示すように、左上の頂点から開始し、まずその頂点のすべての隣接頂点を走査し、次に次の頂点のすべての隣接頂点を走査し、以下同様に、すべての頂点訪問されるまで続けます。
**幅優先走査は、近いところから遠いところへ向かう走査方法であり、ある頂点から出発して、常に最も近い頂点を優先して訪問し、層ごとに外側へ広がっていきます**以下の図に示すように、左上の頂点から出発し、まずその頂点のすべての隣接頂点を走査し、次に次の頂点のすべての隣接頂点を走査し、これを繰り返して、すべての頂点訪問るまで続けます。
![グラフの幅優先走査](graph_traversal.assets/graph_bfs.png)
### アルゴリズムの実装
BFSは通常キューの助けを借りて実装されます(下記のコード参照)。キューは「先入先出」で、これは「近くから遠くへ」走査するBFSの考え方と一致します。
BFS は通常キューを用いて実装され、コードは以下のとおりです。キューは「先入先出し」という性質を持ち、これは BFS の「近いところから遠いところへ」という考え方と本質的に一致しています。
1. 開始頂点`startVet`をキューに追加し、ループを開始します。
2. ループの各反復で、キュー先頭の頂点をポップし、それを訪問済みとして記録し、次にその頂点のすべての隣接頂点をキューの末尾に追加します。
3. すべての頂点が訪問されるまで手順`2.`を繰り返します。
1. 走査の開始頂点 `startVet` をキューに追加し、ループを開始します。
2. ループの各反復で、キュー先頭の頂点を取り出して訪問を記録し、その後その頂点のすべての隣接頂点をキューの末尾に追加します。
3. 手順 `2.` を繰り返し、すべての頂点が訪問されると終了します。
頂点の再訪問を防ぐために、ハッシュセット`visited`を使用してどのノードが訪問されたかを記録します。
頂点の重複走査を防ぐために、どの頂点が訪問済みかを記録するハッシュ集合 `visited` を用います。
!!! tip
ハッシュ集合は、`value` を持たず `key` だけを格納するハッシュテーブルとみなせます。これは $O(1)$ の時間計算量で `key` の追加・削除・検索・更新を行えます。`key` の一意性にもとづき、ハッシュ集合は通常、データの重複排除などの場面で用いられます。
```src
[file]{graph_bfs}-[class]{}-[func]{graph_bfs}
```
コードは比較的抽象的ですが、下図と比較することでより良く理解できます。
コードはやや抽象的なので、以下の図と照らし合わせて理解を深めることを勧めます。
=== "<1>"
![グラフの幅優先探索の手順](graph_traversal.assets/graph_bfs_step1.png)
![グラフの幅優先走査の手順](graph_traversal.assets/graph_bfs_step1.png)
=== "<2>"
![graph_bfs_step2](graph_traversal.assets/graph_bfs_step2.png)
@@ -59,39 +63,39 @@ BFSは通常キューの助けを借りて実装されます(下記のコー
=== "<11>"
![graph_bfs_step11](graph_traversal.assets/graph_bfs_step11.png)
!!! question "幅優先走査のシーケンスは一意ですか?"
!!! question "幅優先走査の順序列は一意ですか?"
一意ではありません。幅優先走査は「近くから遠く」の順で走査することのみを要求し、**同じ距離の頂点の走査順は任意にできます**。例えば、上図では、頂点$1$$3$の訪問順序を交換できますし、頂点$2$、$4$、$6$の順序も同様です。
一意ではありません。幅優先走査は「近いところから遠いところへ」の順で走査することだけを要求し、**同じ距離にある複数の頂点の走査順は任意に入れ替えて構いません**。上図を例にすると、頂点 $1$$3$ の訪問順交換でき、頂点 $2$、$4$、$6$ の訪問順も任意に入れ替えられます。
### 計算量分析
### 計算量分析
**時間計算量**:すべての頂点が一度ずつエンキューおよびデキューされ、$O(|V|)$時間を使用します。隣接頂点を走査する過程で、無向グラフであるため、すべての辺が$2$回訪問され、$O(2|E|)$時間を使用します。全体で$O(|V| + |E|)$時間を使用します。
**時間計算量**:すべての頂点は 1 回ずつキューに入り、1 回ずつキューから出るため、$O(|V|)$ 時間す。隣接頂点を走査する過程で、無向グラフであるため、すべての辺が $2$ 回訪問され、$O(2|E|)$ 時間です。したがって全体で$O(|V| + |E|)$ 時間す。
**空間計算量**:リスト`res`、ハッシュセット`visited`、キュー`que`の最大頂点数は$|V|$で、$O(|V|)$空間を使用します。
**空間計算量**:リスト `res`、ハッシュ集合 `visited`、キュー `que` に含まれる頂点数は最大で $|V|$ であるため、$O(|V|)$ 空間す。
## 深さ優先探索
## 深さ優先走査
**深さ優先探索は可能な限り遠くまで行き、それ以上のパスがない場合にバックトラックする走査方法です**図に示すように、左上の頂点から開始し、それ以上のパスがなくなるまで現在の頂点のいずれかの隣接頂点を訪問し、次に戻って続行し、すべての頂点走査されるまで続けます。
**深さ優先走査は、まず行けるところまで進み、進めなくなったら戻る走査方法です**以下の図に示すように、左上の頂点から出発し、現在の頂点のある隣接頂点を訪問して、行き止まりに達するまで進んだら戻り、再び別の方向へ進んで行き止まりまで進んで戻る、ということを繰り返し、すべての頂点走査が完了するまで続けます。
![グラフの深さ優先走査](graph_traversal.assets/graph_dfs.png)
### アルゴリズムの実装
この「可能な限り遠くまで行ってから戻る」アルゴリズムパラダイムは通常再帰にづいて実装されます。幅優先探索と同様に、深さ優先探索でも、訪問を避けるために訪問済み頂点を記録するハッシュセット`visited`の助けが必要です。
この「行き止まりまで進んでから戻る」アルゴリズムのパターンは、通常再帰にもとづいて実装されます。幅優先走査と同様に、深さ優先走査でも、頂点の重複訪問を避けるために訪問済み頂点を記録するハッシュ集合 `visited` を用います。
```src
[file]{graph_dfs}-[class]{}-[func]{graph_dfs}
```
深さ優先探索のアルゴリズムプロセスを下図に示します。
深さ優先走査のアルゴリズムの流れは以下の図のとおりです。
- **破線は下向きの再帰を表し**、新しい頂点を訪問するために新しい再帰メソッドが開始されたことをします。
- **曲線の破線は上向きのバックトラックを表し**、この再帰メソッドがこのメソッドが開始された位置に戻ったことをします。
- **直線の破線は下向きの再帰呼び出し**を表し、新しい頂点を訪問するために新たな再帰メソッドが開始されたことを意味します。
- **曲線の破線は上向きのバックトラック**を表し、この再帰メソッドがすでに戻って、呼び出し元の位置までたどり着いたことを意味します。
理解を深めるため、下図とコードを組み合わせて、DFSプロセス全体を頭の中でシミュレート(または描画)することを勧めます。各再帰メソッドがいつ開始され、いつ戻るか含めてです
理解を深めるために、以下の図とコードを結びつけて、DFS 全体の過程を頭の中でシミュレーションする(あるいは紙に書き出す)ことを勧めます。各再帰メソッドがいつ開始、いつ戻るか含めて追ってみてください
=== "<1>"
![グラフの深さ優先探索の手順](graph_traversal.assets/graph_dfs_step1.png)
![グラフの深さ優先走査の手順](graph_traversal.assets/graph_dfs_step1.png)
=== "<2>"
![graph_dfs_step2](graph_traversal.assets/graph_dfs_step2.png)
@@ -123,14 +127,14 @@ BFSは通常キューの助けを借りて実装されます(下記のコー
=== "<11>"
![graph_dfs_step11](graph_traversal.assets/graph_dfs_step11.png)
!!! question "深さ優先走査のシーケンスは一意ですか?"
!!! question "深さ優先走査の順序列は一意ですか?"
幅優先走査と同様に、深さ優先走査シーケンスの順序も一意ではありません。ある頂点が与えられた場合、どの方向を最初に探索することも可能です。つまり隣接頂点の順序は任意にシャッフルできますが、すべて深さ優先走査の一部です。
幅優先走査と同様に、深さ優先走査の順序も一意ではありません。ある頂点が与えられたとき、どの方向をに探索してもよく、つまり隣接頂点の順序は任意に入れ替えられ、それでも深さ優先走査になります。
木の走査を例にすると、「根 $\rightarrow$ 左 $\rightarrow$ 右」「左 $\rightarrow$ 根 $\rightarrow$ 右」「左 $\rightarrow$ 右 $\rightarrow$ 根」は、それぞれ先行順、中間順、後行順走査に対応します。これらは 3 種類の走査優先順位を示していますが、いずれも深さ優先走査に属します。
木の走査を例に取ると、「根 $\rightarrow$ 左 $\rightarrow$ 右」、「左 $\rightarrow$ 根 $\rightarrow$ 右」、「左 $\rightarrow$ 右 $\rightarrow$ 根」は、それぞれ前順、中順、後順走査に対応します。これらは3つの異なる走査優先度を示していますが、3つすべてが深さ優先走査と見なされます。
### 計算量の分析
### 計算量分析
**時間計算量**:すべての頂点は $1$ 回ずつ訪問されるため、$O(|V|)$ 時間です。すべての辺は $2$ 回ずつ訪問されるため、$O(2|E|)$ 時間です。したがって全体では $O(|V| + |E|)$ 時間です。
**間計算量**すべての頂点が一度訪問され、$O(|V|)$時間を使用します。すべての辺が2回訪問され、$O(2|E|)$時間を使用します。全体で$O(|V| + |E|)$時間を使用します。
**空間計算量**:リスト`res`、ハッシュセット`visited`の最大頂点数は$|V|$で、最大再帰深度は$|V|$です。したがって、$O(|V|)$空間を使用します。
**間計算量**リスト `res` とハッシュ集合 `visited` に含まれる頂点数は最大で $|V|$ であり、再帰の深さも最大で $|V|$ であるため、$O(|V|)$ 空間です。
+3 -3
View File
@@ -4,6 +4,6 @@
!!! abstract
人生の旅路において、私たちの一人一人はノードであり、無数の見えない辺結ばれています。
一つ一つの出会いと別れ、この大な人生のグラフに独特の印を残していきます。
人生の旅路において、私たちはそれぞれ一つひとつのノードのようなものであり、無数の見えない辺によって結ばれています。
出会いと別れのたびに、この大なネットワークグラフの中に固有の足跡が刻まれます。
+19 -19
View File
@@ -1,31 +1,31 @@
# まとめ
### 重要な復習
### 重要なポイントの振り返り
- グラフは頂点と辺構成されます。頂点の集合と辺の集合として記述できます。
- 線形関係(連結リストなど)や階層関係(木など)と比較して、ネットワーク関係(グラフ)はより大きな柔軟性を提供し、より複雑になります。
- 有向グラフでは、辺に方向があります。連結グラフでは、任意の頂点から他の任意の頂点に到達できます。重み付きグラフでは、各辺に関連する重み変数があります。
- 隣接行列は行列(2次元配列)を使用してグラフを表現する方法です。行と列は頂点を表します。行列要素の値は、2つの頂点間に辺があるかどうかを示し、辺がある場合は$1$、ない場合は$0$を使用します。隣接行列は辺の追加削除、チェックなどの操作に非常に効率的ですが、より多くのスペースが必要です。
- 隣接リストは連結リストの集合を使用してグラフを表現するもう一つの一般的な方法です。グラフ内の各頂点には、その隣接するすべての頂点を含むリストがあります。$i$番目のリストは頂点$i$を表します。隣接リストは隣接行列と比較してより少ないスペースを使用します。ただし、辺を見つけるためにリストを走査する必要があるため、時間効率は低くなります。
- 隣接リストの連結リストが十分に長くなったとき、ルックアップ効率を向上させるために赤黒木やハッシュテーブルに変換できます。
- アルゴリズム設計の観点から、隣接行列は「空間時間のトレードオフ」の概念を反映し、隣接リストは「時間空間のトレードオフ」を反映します。
- グラフは、ソーシャルネットワークや地下鉄路線など、さまざまな現実世界のシステムをモデル化するために使用できます。
- 木はグラフの特別なケースであり、木の走査もグラフ走査の特別なケースです。
- グラフの幅優先走査は、近くから遠くへと層ごとに拡張する探索方法で、通常キューを使します。
- グラフの深さ優先走査は、それ以上のパスがない場合にバックトラックする前に、まず終端に到達することを優先する探索方法です。しばしば再帰を使用して実装されます。
- グラフは頂点と辺から構成され、一組の頂点と一組の辺からなる集合として表せます。
- 線形関係(連結リスト)や分治関係(木)と比て、ネットワーク関係(グラフ)は自由度が高く、そのぶん複雑です。
- 有向グラフの辺は方向性を持ち、連結グラフでは任意の頂点に到達でき重み付きグラフの各辺は重み変数を含みます。
- 隣接行列は行列を用いてグラフを表し、各行(列)が 1 つの頂点を表し行列要素が辺を表します。$1$ または $0$ を用いて、2 つの頂点間に辺があるかないかを示します。隣接行列は追加削除・検索・更新の操作効率が高い一方で、より多くの空間を消費します。
- 隣接リストは複数の連結リストを使ってグラフを表し、第 $i$ 個の連結リストが頂点 $i$ に対応し、その頂点に隣接するすべての頂点を格納します。隣接リストは隣接行列よりも省スペースですが、辺を探すために連結リストを走査する必要があるため、時間効率は低くなります。
- 隣接リストの連結リストが長くなりすぎた場合は、赤黒木やハッシュテーブルに変換することで、検索効率を高められます。
- アルゴリズムの考え方という観点では、隣接行列は「空間時間と引き換えにする」ことを体現し、隣接リストは「時間空間と引き換えにする」ことを体現します。
- グラフは、ソーシャルネットワークや地下鉄路線など、さまざまな現実のシステムをモデル化するために使ます。
- 木はグラフの特殊な一例であり、木の走査もグラフ走査の特殊な一例です。
- グラフの幅優先探索は、近いところから遠いところへ、層ごとに広がっていく探索方法であり、通常キューを使って実装します。
- グラフの深さ優先探索は、まず行けるところまで進み、進めなくなったらバックトラックする探索方法であり、通常は再帰に基づいて実装ます。
### Q & A
**Q**: パスは頂点のシーケンスとして定義されますか、それとも辺のシーケンスとして定義されますか?
**Q**:経路の定義は頂点列ですか、それとも辺列ですか?
グラフ理論では、グラフ内のパスは頂点のシーケンスを結ぶ有限または無限の辺のシーケンスです。
Wikipedia では言語版ごとに定義が一致していません。英語版では「経路は辺の列」であり、中国語版では「経路は頂点の列」です。以下は英語版の原文です:In graph theory, a path in a graph is a finite or infinite sequence of edges which joins a sequence of vertices.
この文書では、パスは頂点のシーケンスではなく、辺のシーケンスと考えられます。これは、2つの頂点を結ぶ複数の辺がる可能性があり、その場合各辺がパスに対応するためです。
書では、経路を頂点列ではなく辺列とみなします。これは、2 つの頂点の間に複数の辺が存在する可能性があり、その場合各辺がそれぞれ 1 本の経路に対応するためです。
**Q**: 非連結グラフでは、走査できない点がありますか?
**Q**非連結グラフには到達できない点がありますか?
非連結グラフでは、特定の点から到達できない頂点が少なくとも1つあります。非連結グラフを走査するには、グラフのすべての連結成分を走査するために複数の始点を設定する必要があります。
非連結グラフでは、ある頂点から出発すると、少なくとも 1 つの頂点には到達できません。非連結グラフ全体を走査するには、グラフのすべての連結成分をたどれるように複数の始点を設定する必要があります。
**Q**: 隣接リスト、「その頂点に接続されたすべての頂点」の順序は重要ですか?
**Q**隣接リストにおいて、「その頂点に接続されたすべての頂点」の順序に決まりはありますか?
任意の順序で構いません。ただし実際のアプリケーションでは、頂点追加された順序や頂点値の順など、特定のルールに従ってそれらをソートする必要がある場合があります。これにより、特定の極値を持つ頂点を素早く見つけることができます。
順序は任意でかまいません。ただし実際の応用では、頂点追加た順序や頂点値の大小順など、特定の規則に従って並べ替える必要がある場合があります。そうすることで、「ある種の極値を持つ頂点をすばやく見つけやすくなります。