mirror of
https://github.com/krahets/hello-algo.git
synced 2026-11-14 01:38:18 +08:00
* 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
87 lines
6.1 KiB
Markdown
87 lines
6.1 KiB
Markdown
# グラフの基本操作
|
||
|
||
グラフの基本操作には、「辺」に対する操作と「頂点」に対する操作があります。「隣接行列」と「隣接リスト」では、実装方法が異なります。
|
||
|
||
## 隣接行列による実装
|
||
|
||
頂点数 $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>"
|
||

|
||
|
||
=== "<2>"
|
||

|
||
|
||
=== "<3>"
|
||

|
||
|
||
=== "<4>"
|
||

|
||
|
||
=== "<5>"
|
||

|
||
|
||
隣接行列によるグラフの実装を次に示します。
|
||
|
||
```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>"
|
||

|
||
|
||
=== "<2>"
|
||

|
||
|
||
=== "<3>"
|
||

|
||
|
||
=== "<4>"
|
||

|
||
|
||
=== "<5>"
|
||

|
||
|
||
隣接リストの実装を次に示します。実際のコードは、上図と次の点が異なります。
|
||
|
||
- 頂点の追加と削除をしやすくし、コードを簡潔にするため、連結リストの代わりにリスト(動的配列)を使います。
|
||
- 隣接リストをハッシュテーブルに格納します。`key` は頂点のインスタンス、`value` はその頂点に隣接する頂点のリスト(連結リスト)です。
|
||
|
||
また、隣接リストでは `Vertex` クラスで頂点を表します。隣接行列と同じようにリストのインデックスで頂点を区別すると、インデックス $i$ の頂点を削除する際に、隣接リスト全体を走査し、$i$ より大きいインデックスをすべて $1$ ずつ減らす必要があり、非効率だからです。各頂点を固有の `Vertex` インスタンスにすれば、ある頂点を削除しても、ほかの頂点を変更する必要がありません。
|
||
|
||
```src
|
||
[file]{graph_adjacency_list}-[class]{graph_adj_list}-[func]{}
|
||
```
|
||
|
||
## 効率の比較
|
||
|
||
グラフに $n$ 個の頂点と $m$ 本の辺があるとします。下表で、隣接行列と隣接リストの時間効率と空間効率を比較します。隣接リスト(連結リスト)は本節の実装に対応し、隣接リスト(ハッシュテーブル)は、すべての連結リストをハッシュテーブルに置き換えた実装を指します。
|
||
|
||
<p align="center"> 表 <id> 隣接行列と隣接リストの比較 </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 回で済むため、辺の操作はより高速です。全体として、隣接行列は「空間を使って時間を節約する」考え方を、隣接リストは「時間を使って空間を節約する」考え方を表しています。
|