Files
hello-algo/ja/docs/chapter_graph/graph_operations.md
Yudong Jin 7257641e0e Retranslate the complete Japanese online edition from Chinese (#2008)
* Retranslate the complete Japanese online edition from Chinese

* Fit Japanese homepage labels and localize heading permalinks

* Point Japanese contribution instructions to the Japanese preview

* Replace locale conditionals with multilingual UI tables
2026-10-30 02:12:26 +08:00

87 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# グラフの基本操作
グラフの基本操作には、「辺」に対する操作と「頂点」に対する操作があります。「隣接行列」と「隣接リスト」では、実装方法が異なります。
## 隣接行列による実装
頂点数 $n$ の無向グラフに対する各種操作は、下図のように実装できます。
- **辺の追加・削除**:隣接行列の指定した辺を直接変更するだけなので、$O(1)$ 時間です。無向グラフのため、両方向の辺を同時に更新する必要があります。
- **頂点の追加**:隣接行列の末尾に行と列を一つずつ追加し、すべて $0$ で埋めます。$O(n)$ 時間かかります。
- **頂点の削除**:隣接行列から行と列を一つずつ削除します。最悪の場合は先頭の行と列を削除するときで、$(n-1)^2$ 個の要素を「左上に移動」するため、$O(n^2)$ 時間かかります。
- **初期化**:$n$ 個の頂点を受け取り、長さ $n$ の頂点リスト `vertices` を $O(n)$ 時間で初期化します。$n \times n$ の隣接行列 `adjMat` の初期化には $O(n^2)$ 時間かかります。
=== "<1>"
![隣接行列の初期化、辺の追加・削除、頂点の追加・削除](graph_operations.assets/adjacency_matrix_step1_initialization.png)
=== "<2>"
![隣接行列で辺を追加](graph_operations.assets/adjacency_matrix_step2_add_edge.png)
=== "<3>"
![隣接行列で辺を削除](graph_operations.assets/adjacency_matrix_step3_remove_edge.png)
=== "<4>"
![隣接行列で頂点を追加](graph_operations.assets/adjacency_matrix_step4_add_vertex.png)
=== "<5>"
![隣接行列で頂点を削除](graph_operations.assets/adjacency_matrix_step5_remove_vertex.png)
隣接行列によるグラフの実装を次に示します。
```src
[file]{graph_adjacency_matrix}-[class]{graph_adj_mat}-[func]{}
```
## 隣接リストによる実装
無向グラフの頂点数を $n$、辺の総数を $m$ とすると、下図の方法で各種操作を実装できます。
- **辺の追加**:頂点に対応する連結リストの末尾に辺を追加するだけなので、$O(1)$ 時間です。無向グラフのため、両方向の辺を同時に追加する必要があります。
- **辺の削除**:頂点に対応する連結リストで指定した辺を探して削除するため、$O(m)$ 時間かかります。無向グラフでは、両方向の辺を同時に削除する必要があります。
- **頂点の追加**:隣接リストに連結リストを一つ追加し、新しい頂点をその先頭ノードにします。$O(1)$ 時間です。
- **頂点の削除**:隣接リスト全体を走査し、指定した頂点を含むすべての辺を削除するため、$O(n + m)$ 時間かかります。
- **初期化**:隣接リストに $n$ 個の頂点と $2m$ 本の辺を作るため、$O(n + m)$ 時間かかります。
=== "<1>"
![隣接リストの初期化、辺の追加・削除、頂点の追加・削除](graph_operations.assets/adjacency_list_step1_initialization.png)
=== "<2>"
![隣接リストで辺を追加](graph_operations.assets/adjacency_list_step2_add_edge.png)
=== "<3>"
![隣接リストで辺を削除](graph_operations.assets/adjacency_list_step3_remove_edge.png)
=== "<4>"
![隣接リストで頂点を追加](graph_operations.assets/adjacency_list_step4_add_vertex.png)
=== "<5>"
![隣接リストで頂点を削除](graph_operations.assets/adjacency_list_step5_remove_vertex.png)
隣接リストの実装を次に示します。実際のコードは、上図と次の点が異なります。
- 頂点の追加と削除をしやすくし、コードを簡潔にするため、連結リストの代わりにリスト(動的配列)を使います。
- 隣接リストをハッシュテーブルに格納します。`key` は頂点のインスタンス、`value` はその頂点に隣接する頂点のリスト(連結リスト)です。
また、隣接リストでは `Vertex` クラスで頂点を表します。隣接行列と同じようにリストのインデックスで頂点を区別すると、インデックス $i$ の頂点を削除する際に、隣接リスト全体を走査し、$i$ より大きいインデックスをすべて $1$ ずつ減らす必要があり、非効率だからです。各頂点を固有の `Vertex` インスタンスにすれば、ある頂点を削除しても、ほかの頂点を変更する必要がありません。
```src
[file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{}
```
## 効率の比較
グラフに $n$ 個の頂点と $m$ 本の辺があるとします。下表で、隣接行列と隣接リストの時間効率と空間効率を比較します。隣接リスト(連結リスト)は本節の実装に対応し、隣接リスト(ハッシュテーブル)は、すべての連結リストをハッシュテーブルに置き換えた実装を指します。
<p align="center"> 表 <id> &nbsp; 隣接行列と隣接リストの比較 </p>
| | 隣接行列 | 隣接リスト(連結リスト) | 隣接リスト(ハッシュテーブル) |
| ------------ | -------- | -------------- | ---------------- |
| 隣接しているかの判定 | $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 回で済むため、辺の操作はより高速です。全体として、隣接行列は「空間を使って時間を節約する」考え方を、隣接リストは「時間を使って空間を節約する」考え方を表しています。