Upload
donhu
View
221
Download
0
Embed Size (px)
Citation preview
データ構造・アルゴリズム論 I 解説資料集
アルゴリズムの表現
オーダー記法
列の操作
スタック と キュー の操作
辞書 と 順位付きキュー の操作
木の用語
関数値の増え方の比較
平衡探索木
2-3-4木の基本操作
2分木の回転
2分木の走査
2分木の 再帰構造と走査
整列
分割統治法による整列
グラフのデータ構造
グラフの深さ優先探索
グラフの幅優先探索
無向グラフの関節点と 2連結成分
無向グラフの 2連結成分の計算
山田 俊行
http://www.cs.info.mie-u.ac.jp/~toshi/lectures/algorithm/
2017年 1月
1
データ構造・アルゴリズム論 I 第 1回 解説資料 (山田)
アルゴリズムの表現 (教科書 1.1, 1.3 節)
アルゴリズムは,現実のプログラミング言語を使って表せる.この資料では,アルゴリズムの例として,
最大公約数の計算と多項式の値の計算の二つを,Cプログラムとして記述する.
●最大公約数を求めるアルゴリズム
仕様
入力 正整数 m, n
出力 m と n の最大公約数
反復を使ったプログラム
1 int gcd(int m, int n) {
2 while (n > 0) {
3 int rem = m % n;
4 m = n;
5 n = rem;
6 }
7 return m;
8 }
再帰を使ったプログラム (教科書 p. 8 図 1.1)
1 int gcd(int m, int n) {
2 if (n == 0) return m;
3 else return gcd(n, m % n);
4 }
2
●多項式の値を求めるアルゴリズム
仕様
入力 非負整数 n, 整数配列 a[0..n],整数 x
出力 多項式 anxn + · · ·+ a1x
1 + a0x0 の値
定義に沿ったプログラム
1 int eval (int n, int a[], int x) {
2 int val = 0, i;
3 for (i = n; i >= 0; i--) {
4 int pow = 1, j;
5 for (j = 1; j <= i; j++)
6 pow *= x;
7 val += a[i] * pow; /* pow == x^i */
8 }
9 return val;
10 }
ホーナー法によるプログラム (確認問題 1 問 2(1))
1 int eval (int n, int a[], int x) {
2 int val = 0, i;
3 for ( )
4 val = ;
5 return val;
6 }
3
データ構造・アルゴリズム論 I 第 2回 解説資料 (山田)
オーダー記法 (教科書 1.2節)
オーダー記法を使えば,計算量の増え方の度合いを簡潔に表せる.
●オーダー記法の定義
定義 (教科書 p. 4)
f(n) は O(g(n))
:⇔ 正実数 cと非負整数mが存在し,
任意の非負整数 nについて,n ≥ m ならば f(n) ≤ c g(n)
⇔ ∃c ∈ R+ ∃m ∈ N ∀n ∈ N (n ≥ m ⇒ f(n) ≤ c g(n))
※ R+ は正実数全体の集合を表し,Nは非負整数全体の集合を表す.
例 1
f(n) = (n2 + 5n+ 4)/2 のとき,f(n)は O(n2).
【方針】ある非負整数以上の nで常に f(n) ≤ c n2,となる正実数 cを見つける.cとして
は,係数の和 1/2 + 5/2 + 2 をとる.
【証明】n ≥ 1 なら常に 1 ≤ n ≤ n2 だから,
f(n) = (n2 + 5n+ 4)/2
= (1/2)n2 + (5/2)n+ 2
≤ (1/2 + 5/2 + 2)n2
= 5n2.
よって,c = 5 とおけば,n ≥ 1 で常に f(n) ≤ c n2.
例 2
f(n) = (n2 + 5n+ 4)/2 のとき,f(n) は O(n) ではない.
【方針】背理法による.
【証明】f(n) が O(n) であると仮定して矛盾を導く.この仮定と O 記法の定義より,正実
数 cと非負整数mが存在し,任意の非負整数 nについて,n ≥ m ならば f(n) ≤ c n.こ
こで,f(n) ≤ c n
⇔ (n2 + 5n+ 4)/2 ≤ c n
⇔ n2 + 5n+ 4 ≤ 2 c n
⇔ n(n− (2c− 5)) ≤ −4.
2c − 5は非負整数とは限らないので,n = max(m, ⌈2c − 5⌉) とおくと,nは非負整数で
n ≤ m を満たすから,上記の不等式が成り立つ.一方,nの定め方より nと n− (2c− 5)
は非負だから,その積 n(n− (2c− 5)) も非負であり,上記の不等式に矛盾する.
※ ⌈x⌉は x以上の最小整数を表す.
(別の証明法の解説が裏面にある)
4
●別の証明法
例 3
f(n) = (n2 + 5n+ 4)/2 のとき,f(n) は O(n2).
【方針】ある非負整数以上の nで常に f(n) ≤ c n2,となる正実数 cを見つける.
cとしては,最大次数の項の係数 1/2を超える最小の整数である 1 をとる.
【証明】c = 1とおく.このとき,
f(n) ≤ c n2
⇔ n2 + 5n+ 4 ≤ 2n
⇔ n2 − 5n− 4 ≥ 0
だから,2次方程式の解の公式を使えば,n ≥ (5 +√41)/2 で常に f(n) ≤ c n2であると分
かる.よって,n ≥ 6 で常に f(n) ≤ c n2.
性質
f(n) は O(g(n)) ではない⇔ ¬ f(n) は O(g(n))
⇔ ¬ ∃c ∈ R+ ∃m ∈ N ∀n ∈ N (n ≥ m ⇒ f(n) ≤ c g(n))
⇔ ∀c ∈ R+ ∀m ∈ N ∃n ∈ N (n ≥ m ∧ f(n) > c g(n))
⇔ 任意の正実数 cと非負整数mについて,
非負整数 nが存在し,n≧m かつ f(n)> cg(n).
例 4
f(n) = (n2 + 5n+ 4)/2 のとき,f(n) は O(n) ではない.
【方針】任意の正実数 cと非負整数mについて,n ≥ m かつ f(n) > cn を満たす非負整
数 n を見つける.
【証明】cを任意の正実数,mを任意の非負整数とする.
f(n) > cn
⇔ (n2 + 5n+ 4)/2 > cn
⇔ n2 + (5 − 2c)n+ 4 > 0
であり,ここで実数関数 f ′ を f ′(x) = x2 + (5 − 2c)x+ 4 と定める.
• 2次方程式 f ′(x) = 0に実数解がないとき,任意の整数 nについて n2+(5−2c)n+4 > 0
だから,n = m とおけば,n ≥ m かつ f(n) > cn.
• 2次方程式 f ′(x) = 0 に実数解 x, x′ (ただし x ≤ x′) があるとき
n > x′ を満たす任意の非負整数 n について n2 + (5 − 2c)n+ 4 > 0 だから,x′ を超え
る最小の非負整数を m′ と表して n = max(m,m′) とおけば,n ≥ m かつ f(n) > cn.
5
データ構造・アルゴリズム論 I 第 3回 解説資料 (山田)
列の操作 (教科書 2.1節)
列 (リスト)のデータ構造を使えば,ものの並びを操作できる.
●列の操作
基本操作 (教科書 p. 16)
生成 create()
挿入 insert(l, k, a) l … 列 (リスト) k … 位置 a … 要素
削除 delete(l, k)
参照 access(l, k)
空判定 empty(l)
部分列 sub(l, k)
他の操作
要素数,要素の探索,要素の更新,全体の複製,など
例
l← create()−−−−−−−−−−−→ l = [ ]
insert(l, 1, 10)
insert(l, 2, 20)
insert(l, 3, 30)−−−−−−−−−−−→ l = [10, 20, 30] 位置は 1から
insert(l, 2, 500)−−−−−−−−−−−→ l = [10, 500, 20, 30] 2番目が 500
delete(l, 2)−−−−−−−−−−−→ l = [10, 20, 30]
access(l, 3)−−−−−−−−−−−→ 30 l = 〃
empty(l)−−−−−−−−−−−→ 偽 l = 〃
l′ ← sub(l, 2)−−−−−−−−−−−→ l = 〃 l′ = [20, 30] l は変化なし
l′ は第 2要素以降の列l′ ← sub(l, 4)−−−−−−−−−−−→ l = 〃 l′ = [ ]
empty(l′)−−−−−−−−−−−→ 真 l = 〃 l′ =〃
6
データ構造・アルゴリズム論 I 第 4回 解説資料 (山田)
スタック と キュー の操作 (教科書 2.2, 2.3 節)
スタックとキューを使うと,列に対する操作位置を限定して効率化を図れる.
●スタックの操作
基本操作 (教科書 p. 19)
生成 create()
(最上段への) 挿入 push(s, a) s … スタック a … 要素
(最上段からの)削除 pop(s)
(最上段の) 参照 top(s)
空判定 empty(s)
例 (スタックの最上段を列の末尾として表示)
s = [10, 20, 30]
push(s, 40)−−−−−−−−→ s = [10, 20, 30, 40]
pop(s)−−−−−−−−→ s = [10, 20, 30]
top(s)−−−−−−−−→ 30 s = 〃 s は変化なし
empty(s)−−−−−−−−→ 偽 s = 〃
●キューの操作
基本操作 (教科書 p. 21)
生成 create()
(末尾への) 追加 insert(q, a) q … キュー a … 要素
(先頭からの) 削除 delete(q)
(先頭の) 参照 top(q)
空判定 empty(q)
例
q = [10, 20, 30]
insert(q, 40)−−−−−−−−→ q = [10, 20, 30, 40]
delete(q)−−−−−−−−→ q = [20, 30, 40]
top(q)−−−−−−−−→ 20 q = 〃 q は変化なし
empty(q)−−−−−−−−→ 偽 q = 〃
7
データ構造・アルゴリズム論 I 第 5回 解説資料 (山田)
辞書 と 順位付きキュー の操作 (教科書 4.2, 2.4 節)
辞書は,データの検索 (データを格納しているかの判定)ができるデータ構造であり,順位付きキューは,
優先度の高いデータから順に取り出せるデータ構造である.
●辞書の操作
基本操作 (教科書 p. 52)
生成 create()
挿入 insert(d, a) d … 辞書 a … 要素
削除 delete(d, a)
所属判定 member(d, a)
空判定 empty(d)
例 (辞書を集合として表示)
d = { 10, 20, 40 }insert(d, 30)−−−−−−−−−−−→ d = { 10, 20, 30, 40 }delete(d, 20)−−−−−−−−−−−→ d = { 10, 20, 40 }member(d, 20)−−−−−−−−−−−→ 真 d = 〃
member(d, 30)−−−−−−−−−−−→ 偽 d = 〃
●順位付きキューの操作
基本操作 (教科書 p. 23)
生成 create()
挿入 insert(p, a) p … 順位付きキュー a … 要素
(最小値の)削除 deletemin(p)
(最小値の)参照 findmin(p)
空判定 empty(p)
例 (順位付きキューを集合として表示)
p = { 10, 20 }insert(p, 40)−−−−−−−−−−−→ p = { 10, 20, 40 }insert(p, 30)−−−−−−−−−−−→ p = { 10, 20, 30, 40 }deletemin(p)−−−−−−−−−−−→ p = { 20, 30, 40 }findmin(p)−−−−−−−−−−−→ 20 p = 〃
8
データ構造・アルゴリズム論 I 第 6回 解説資料 (山田)
関数値の増え方の比較 (教科書 1.2 節)
計算量の見積もりに使う様々な関数を,値の増え方の観点から比較する.
●関数値の増え方の大小関係
関数の値の増え方の大小関係 ≪ を
f(n)≪ g(n) ⇐⇒ f(n)が O(g(n))であり,かつ,g(n)が O(f(n))ではない
により定めると,以下の大小関係が成り立つ.
1≪ log2 n≪√n≪ n≪ n log2 n≪ n2 ≪ 2n ≪ n!
この関係は,次の両対数グラフでも確認できる (参考:教科書 p. 6).
1
10
100
1000
10000
100000
1 10 100 1000 10000 100000
n! 2n n2 n log2 n n
n
√n
log2 n
1
9
データ構造・アルゴリズム論 I 第 8回 解説資料 (山田)
平衡探索木 (教科書 4.3節)
代表的な平衡探索木である 2色木を,その基礎である 2-3-4木と共に理解する.
● 2-3-4木
定義
以下の条件を満たす木
• 葉の深さが全て同一• 頂点に 1 ∼ 3個の要素を保持
• 子をもつならその数は (頂点の要素数) + 1 (つまり 2 ∼ 4個)
• 各要素について (左の子孫の要素) < (要素) < (右の子孫の要素)
例 (40)
(20) (50,60,75)
(10,15) (25,30,35) (45) (55) (65,70) (80,85)
「2-3-4木」は略称で,「平衡 2-3-4探索木」が正式名称.
● 2色木
定義
以下の条件を満たす,辺に赤か黒の色の付いた 2分木
• 葉から根に向けて赤辺が続かない• 子が一つ以下のどの頂点から出発しても, 根までに通る黒辺の数が同一
• 各頂点について (左の子孫) < (頂点) < (右の子孫)
例 40
20 60
15 30 50 75
10 25 35 45 55 65 80
70 85
赤辺を二本線 で,黒辺を一本線 で表す.
10
● 2-3-4木と 2色木の対応
2-3-4木 (x) (x, y) (x, y, z)
2色木x y
x
x
y
または
または y
x z
●参考文献
R. セジウィック,『アルゴリズム C・新版—基礎・データ構造・整列・探索』,近代科学社,2004.
13.3節 (トップダウン 2-3-4木)と 13.4節 (赤黒木)を参照.
11
データ構造・アルゴリズム論 I 第 9回 解説資料 (山田)
2-3-4木の基本操作の手順
平衡木のうち理解しやすい 2-3-4木の基本操作のアルゴリズムについて理解する.
● 2-3-4木の基本操作
所属判定 (要素の探索)
2分探索と同様 (複数要素の頂点では逐次探索)
挿入
探索で通る各 3要素頂点を,分割して中央値を親へ移し,
探索の終点 (2要素以下の葉)に加える
(1, 6)�
���
(0) (2, 4, 5) (8, 10)
+7−→(1, 6)
��
��
(0) (2, 4, 5) ( , 8, 10)
+3−→(1, , 6)
��
�����
AAA
QQQQ
(0) (2, ) (5) (7, 8, 10)
+9−→
(4)�
��@@@
(1)�
��AAA
(6, )�
��AAA
(0) (2, 3) (5) (7) ( , 10)
削除
(授業では扱わない)
12
データ構造・アルゴリズム論 I 第 10回 解説資料 (山田)
2分木の回転 (教科書 4.3節)
2分木を変形するための基本操作である回転について理解する.
● 2分木の回転
回転 (rotation)
y�
��@@@x
��
�
@@@
AAAAA
��
��BAAAA
��
��
CAAAA
��
��
yのまわりの右回転−−−−−−−−−−→←−−−−−−−−−−xのまわりの左回転
x�
��
@@@
AAAAA
����
y�
��
@@@
BAAAA
��
��CAAAA
����
応用例 (2色木の平坦化:確認問題 8問 1)
10
���
�8
���
�7
平坦化−−−−−−−→10での右回転
8
��
@@
��
@@
7 10
1
@@
��
@@
0 6
���
�@@
4���
AAA
8���
AAA
���
AAA
2 5 7 10
平坦化−−−−−−−−→
4����
HHHH����
HHHH1
��
@@
6�
�@@
0 2 5 8���
AAA
���
AAA
7 10
13
データ構造・アルゴリズム論 I 第 9回 解説資料 (山田)
2分木の走査 (教科書 4章 演習問題 pp. 76–77)
2分木の頂点や辺を順に処理するための,走査のアルゴリズムを理解する.
● 2分木の走査
走査 (traverse)
各要素を処理しながら,データ構造全体を辿ること
2
1 6
4 8
3 5 7
先行順(行きがけ順) (preorder)
根 −→ 左部分木 −→ 右部分木
2 1 6 4 3 5 8 7
中間順(通りがけ順) (inorder)
左部分木 −→ 根 −→ 右部分木
1 2 3 4 5 6 7 8
後行順(帰りがけ順) (postorder)
左部分木 −→ 右部分木 −→ 根
1 3 5 4 7 8 6 2
14
データ構造・アルゴリズム論 I 第 10回 解説資料 (山田)
2分木の再帰構造と走査 (教科書 4章 演習問題 pp. 76–77)
2分木の再帰構造に基づく走査アルゴリズムを理解する.
● 2分木の再帰構造
空 空 根
左部分木 右部分木
根
左部分木右部分木
根
左部分木右部分木
根
左部分木右部分木
根
左部分木 右部分木
20
10 40
空 空 30 空
空 空
20
10 40
30
● 2分木の走査アルゴリズム
1 traverse (T) {
2 if (T が空) return
3 visit(T の根) /*先行順*/
4 traverse (T の左部分木)
5 visit(T の根) /*中間順*/
6 traverse (T の右部分木);
7 visit(T の根) /*後行順*/
8 }
1 traverse (T) {
2 visit(T の根) /*先行順*/
3 if (T が左の子をもつ) traverse (T の左部分木)
4 visit(T の根) /*中間順*/
5 if (T が右の子をもつ) traverse (T の右部分木)
6 visit(T の根) /*後行順*/
7 }
15
データ構造・アルゴリズム論 I 第 10回 解説資料 (山田)
整列 (教科書 3章)
データを整列する各種のアルゴリズムの,処理手順と計算量について学ぶ.
●整列の基礎
整列 (sorting)
データ列を指定された順序で並べ替えること
キー (key)
比較のために直接参照するデータの項目
整列法の分類
方法 名前
分類法 バケットソート,基数ソート
選択法 単純選択ソート,ヒープソート
挿入法 単純挿入ソート
交換法 バブルソート
分割統治法 マージソート,クイックソート
●整列アルゴリズム
バケットソート (教科書 p. 29)
(1) m個のキュー (バケット)を生成
(2) 入力を走査し,各データをキー値の番号のキューに挿入
(3) 番号順にキューからデータを全て取り出す
基数ソート (教科書 p. 31)
k番目から 1番目に向けて,各位置での値をキーとして,
バケットソート (などの安定な整列)を反復
(単純)選択ソート (教科書 p. 32 図 3.2)
未整列部分の最小値を整列済みの列に追加することを反復
ヒープソート (教科書 p. 46 図 3.11)
(1) 空のヒープにデータを並び順に挿入
(2) ヒープから最小値を取り出すことを反復
(単純)挿入ソート (教科書 p. 33 図 3.3)
未整列のデータ一つを,整列部分の適切な位置に挿入することを反復
16
バブルソート (教科書 p. 35 図 3.4)
データを走査しながら,隣り合う 2値が逆順なら交換するのを,交換がなくなるまで反復
マージソート (教科書 p. 38 図 3.6)
(1) 未整列データの 2分割を反復
(2) 整列済み部分列の統合 (マージ)を反復
クイックソート (教科書 p. 42 図 3.9)
(1) データ群から基準値を選び,値の大小による 2分割を反復
(2) 分割済みの列の連結を反復
●整列アルゴリズムの時間計算量
整列法 最悪の時間計算量
バケットソート O(m+ n) mはバケット数
基数ソート O(k(m+ n)) mはバケット数,kは組の要素数
単純選択ソート O(n2)
ヒープソート O(n logn)
単純挿入ソート O(n2)
バブルソート O(n2)
マージソート O(n logn)
クイックソート O(n2) 平均の時間計算量は O(n logn)
17
データ構造・アルゴリズム論 I 第 12回 解説資料 (山田)
分割統治法による整列 (教科書 3.3, 3.4 節)
分割統治法に基づく二つの整列アルゴリズムを,呼び出し木を使って理解する.
●マージソートの動作例
配列の変化
[5, 6, 3, 7, 4, 1]
[5, 6, 3][7, 4, 1]
[5][6, 3][7, 4, 1]
[5][6][3][7, 4, 1]
[5][3, 6][7, 4, 1]
[3, 5, 6][7, 4, 1]
[3, 5, 6][7][4, 1]
[3, 5, 6][7][4][1]
[3, 5, 6][7][1, 4]
[3, 5, 6][1, 4, 7]
[1, 3, 4, 5, 6, 7]
再帰関数
msort(p, n) {
/* p:最左位置 n:要素数 */
h = n div 2
msort(p, h )
msort(p+h, n-h)
}
呼び出し木msort
(1,6)!!!!!!
aaaaaa(1,3)�
��@@@
(4,3)�
��@@@
(1,1) (2,2)
JJJ
(4,1) (5,2)
JJJ
(2,1) (3,1) (5,1) (6,1)
18
●クイックソートの動作例
配列の変化
[5, 6, 3, 7, 4, 1]
[1, 4, 3][7, 6, 5]
[1][4, 3][7, 6, 5]
[1][3][4][7, 6, 5]
[1][3, 4][7, 6, 5]
[1, 3, 4][7, 6, 5]
[1, 3, 4][5, 6][7]
[1, 3, 4][5][6][7]
[1, 3, 4][5, 6][7]
[1, 3, 4][5, 6, 7]
[1, 3, 4, 5, 6, 7]
再帰関数
qsort(p, q) {
/* p:最左位置 q:最右位置 */
(j, i) = partition(p, q)
qsort(p, j)
qsort(i, q)
}
呼び出し木qsort
(1,6)!!!!!!
aaaaaa(1,3)�
��@@@
(4,6)�
��@@@
(1,1) (2,3)
JJJ
(4,5)
JJJ
(6,6)
(2,2) (3,3) (4,4) (5,5)
19
データ構造・アルゴリズム論 I 第 13回 解説資料 (山田)
グラフの基礎 (教科書 1.4, 7.1 節)
グラフを使うと,列や木よりも自由に,データ (集合要素)の間の関係を表せる.
●グラフのデータ構造
グラフ (graph)
集合の要素間の (2項)関係を,点と点のつながりで表すもの
?>=<89:;A
��
��44
4444
4
?>=<89:;B
��
// ?>=<89:;D
��
?>=<89:;C
TT
44 ?>=<89:;E
/.-,()*+ · · · 頂点
−→ · · · 辺 (有向/無向)
隣接行列 (adjacency matrix)
頂点間の辺の有無を,行列成分の 0, 1 (偽, 真) で表すデータ構造
A B C D E
0 1 2 3 4
A 0
B 1
C 2
D 3
E 4
/.-,()*+ ///.-,()*+i j
隣接リスト (adjacency lists)
頂点から出る辺すべてを列で表すデータ構造
A 0
B 1
C 2
D 3
E 4
/.-,()*+ ///.-,()*+i j
無向グラフの有向グラフによる表現
• 無向辺を双方向の有向辺で表す(無向辺 {i, j} を有向辺 (i, j), (j, i) で表現)
• 無向辺を番号が小さい頂点から大きい頂点への有向辺で代表させる(無向辺 {i, j} を有向辺 (min{i, j},max{i, j}) で表現)
20
●グラフの基本操作の計算量
隣接行列 隣接リスト
辺 (i, j) の有無の判定
頂点 i から出る辺の有無の判定
領域量
21
データ構造・アルゴリズム論 I 第 14回 解説資料 (山田)
グラフの深さ優先探索 (教科書 7.2節)
グラフの深さ優先探索のアルゴリズムを理解する.
頂点や辺を隣接関係に沿って順に処理しながら,条件に合う頂点や辺を見つけられる.
●深さ優先探索 (depth-first search, DFS) のアルゴリズム
1 dfs(v) {
2 v を処理し,Visited に v を挿入3 foreach ( w ∈ (G で v に隣接する頂点全体) ) {
4 (v, w) を処理5 if ( not w ∈ Visited ) {
6 /* (v, w) は深さ優先スパニング木の辺 */
7 dfs(w)
8 }
9 }
10 }
11 main () {
12 G を読み,Visited を生成 /* G はグラフ,Visited は頂点集合 */
13 while ( not v ∈ Visited となる v がある ) {
14 dfs(v)
15 }
16 }
●深さ優先探索の実行例
A
����
����
��
@@@@
@@@@
@@
B
????
????
??C D
E F
~~~~~~~~~~G
22
●グラフの探索プログラム
Python 言語によるプログラム例と実行結果を以下に示す.
$ cat dfs.py # プログラムファイルの内容を表示
def dfs(v):
print(v) # visited vertex
Visited.add(v)
for w in Adjacency[v]:
# print((v,w)) # every edge
if not w in Visited:
print((v,w)) # edge of spanning tree
dfs(w)
def main():
global Adjacency, Visited
Adjacency = { # graph as adjacency lists
’A’:[’B’,’C’,’D’], # A H
’B’:[’A’,’C’,’E’,’F’], # /|\ |
’C’:[’A’,’B’], # / | \ |
’D’:[’A’,’F’,’G’], # B--C D I
’E’:[’B’], # |\ /|
’F’:[’B’,’D’,’G’], # | \ / |
’G’:[’D’,’F’], # E F--G
’H’:[’I’],
’I’:[’H’],
}
Visited = set() # create empty set of vertexes
for v in Adjacency.keys():
if not v in Visited:
dfs(v)
main()
このプログラムの実行結果 (各頂点の表示とスパニング森の各辺の表示) を以下に示す.
$ python dfs.py | tr -d "’" | fmt # 実行結果を短く加工して表示
A (A, B) B (B, C) C (B, E) E (B, F) F (F, D) D (D, G) G I (I, H) H
リスト・集合・辞書 などの基本データ構造があらかじめ用意されている Python 言語を使うと,データ
構造やアルゴリズムの実習が手軽にできる.上記のプログラムでは,隣接リスト Adjacency を,辞書
(キーは頂点,値は頂点リスト) として実現した.最後の for 文で各頂点をアルファベット順に処理する
には,頂点列 Adjacency.keys()を整列済みの列 sorted(Adjacency.keys())に変えればよい.
23
データ構造・アルゴリズム論 I 第 14回 解説資料 (山田)
グラフの幅優先探索 (教科書 7.2節)
グラフの幅優先探索は,深さ優先探索と同様に,頂点や辺を隣接関係に沿って系統的に走査するアルゴ
リズムである.幅優先探索では,開始頂点に近い頂点を優先して探索するため,始点から各頂点への最
短の (通る辺数が最小の)経路を求められる.
●幅優先探索 (breadth-first search, BFS) のアルゴリズム
1 bfs(u) {
2 u を処理し,Visited に u を挿入3 Waiting を生成 /* Waiting はキューを表す頂点列 */
4 Waiting の末尾に u を追加5 while ( not Waiting が空 ) {
6 Waiting の先頭から v を取り出す7 foreach ( w ∈ (G で v に隣接する頂点全体) ) {
8 if ( not w ∈ Visited ) {
9 /* (v, w) は幅優先スパニング木の辺 */
10 w を処理し,Visited に w を挿入11 Waiting の末尾に w を追加12 }
13 }
14 }
15 }
16 main () {
17 G を読み,Visited を生成 /* G はグラフ,Visited は頂点集合 */
18 while ( not u ∈ Visited となる u がある ) {
19 bfs(u)
20 }
21 }
●幅優先探索の実行例
A
����
����
��
@@@@
@@@@
@@
B
????
????
??C D
E F
~~~~~~~~~~G
24
●幅優先探索のプログラム
Python 言語によるプログラム例と実行結果 (各頂点の表示とスパニング森の各辺の表示)を以下に示す.
$ cat bfs.py # プログラムファイルの内容を表示
def bfs(u):
print(u) # visited vertex
Visited.add(u)
Waiting = [u] # queue as list of vertexes
while Waiting:
v = Waiting.pop(0) # dequeue
for w in Adjacency[v]:
# print((v,w)) # every edge
if not w in Visited:
print((v,w)) # edge of spanning tree
print(w) # visited vertex
Visited.add(w)
Waiting.append(w) # enqueue
def main():
global Adjacency, Visited
Adjacency = { # graph as adjacency lists
’A’:[’B’,’C’,’D’], # A H
’B’:[’A’,’C’,’E’,’F’], # /|\ |
’C’:[’A’,’B’], # / | \ |
’D’:[’A’,’F’,’G’], # B--C D I
’E’:[’B’], # |\ /|
’F’:[’B’,’D’,’G’], # | \ / |
’G’:[’D’,’F’], # E F--G
’H’:[’I’],
’I’:[’H’],
}
Visited = set() # create empty set of vertexes
for v in Adjacency.keys():
if not u in Visited:
bfs(u)
main()
$ python bfs.py | tr -d "’" | fmt # 実行結果を短く加工して表示
A (A, B) B (A, C) C (A, D) D (B, E) E (B, F) F (D, G) G I (I, H) H
内側のループ本体の実行回数は,無向辺の数の 2倍 (つまり双方向の有向辺の数)である.これを確かめ
るには,各スパニング木の辺を表示する print((v,w))の行を削除した後,for ループ本体の冒頭の #
を消すことで print((v,w))を有効にしてから,プログラムを実行すればよい.
25
データ構造・アルゴリズム論 I 第 15回 解説資料 (山田)
無向グラフの関節点と 2連結成分 (教科書 7.3節)
連結無向グラフを頂点で分断して連結部分ごとに分けたいとき,関節点や 2連結成分の考え方が役立つ.
●基本用語
関節点付随する辺と共に取り除くと,グラフが非連結になる頂点
2連結成分グラフを関節点で分けて得られる,各部分グラフ
A
@@@@
@@@ B C
����
���
D E F
G H I
◇ · · · 関節点
/. -,() *+ · · · 2連結成分
●深さ優先探索で得る情報
A
@@@@
@@@ B C
����
���
D E F
G H I
'&%$ !"#i · · · 頂点の訪問順位 (dfnum値)
A
@@@@
@@@
@@@@
@@@
B D
E
@@@@
@@@
@@@@
@@@
C H
F G
I
· · · 深さ優先スパニング木 (DFST)の辺
· · · 逆辺 (訪問済み頂点への辺)
※ DFSTの子孫と先祖を結ぶ
26
●関節点に関連する性質や定義
木の用語の注意
以下の解説で使う木の用語 (根,親,子,子孫,先祖)は,DFST についてのもの
A
OOOOOOOOOOO
OOOOOOOOOOO
B D
E
OOOOOOOOOOO
OOOOOOOOOOO
C H
F G
I
· · · DFST の辺
· · · 逆辺/.-,()*+i · · · 訪問順位 (dfnum値)
j · · · L値
訪問順位の性質
• 深さ優先探索による頂点の訪問順は,DFST の先行順走査と同じ
• 訪問順位を dfnum( · )で表すと,vの子 wについて dfnum(v) < dfnum(w)
関節点の性質
vは関節点 ⇐⇒ 次の (1)か (2)が成立
(1) vが根で,複数の子をもつ
(2) vが根以外で,vの子 wのうち「wのどの子孫からも,vの真の先祖への逆辺がない」
という性質を満たすものがある
L値の定義L(v) = min
(
{
dfnum(v)}
∪{
dfnum(u) | vの子孫と uとを結ぶ逆辺がある}
)
L値の性質
v の子 w について,
v > L(w) ⇐⇒ w のある子孫から,v の真の祖先への逆辺がある
v ≤ L(w) ⇐⇒ w のどの子孫からも,v の真の祖先への逆辺がない
関節点の判定
根 · · · 子が複数
根以外 · · · (自分の dfnum値) ≤ (子の L値) となる子がある
27
データ構造・アルゴリズム論 I 第 15回 解説資料 (山田)
無向グラフの 2連結成分の計算 (教科書 7.3節)
深さ優先探索の応用例として,連結無向グラフの 2連結成分を計算するアルゴリズムを学ぶ.
● 2連結成分を求めるアルゴリズム (教科書 p. 115, 確認問題 14)
グラフの深さ優先探索による頂点や辺の走査に基づく.
(1) dfnum値と L値の計算行番号
• 行きがけ dfnumを訪問順位に定め,Lを (仮に)同じ値にする 4, 5
• 逆辺訪問時 逆辺の先の dfnum値の方が小さいなら,Lをその値に更新 19
• 帰りがけ 子 の L値 〃 〃 15
A B
????
??C
D E
????
??F
G H I
A
UUUUUUUUUUUUUUUUUU
B D
C E
F H
TTTTTTTTTTTTTTTTTT
G I
(2) グラフの分断辺の判定
• 帰りがけ dfnum値が子の L値以下かを判定 11
A
UUUUUUUUUUUUUUUUUU
B D
C E
F H
TTTTTTTTTTTTTTTTTT
G I
(3) 2連結成分の計算
• 行きがけ 木の辺をスタックに積む 8
• 逆辺訪問時 逆 辺 〃 18
• 帰りがけ 分断辺の訪問時に,スタック上にある子孫の辺をまとめて取り出す 12, 13
28
● 2連結成分を求めるアルゴリズム (続き)
(3) 2連結成分の計算行番号
• 行きがけ 木の辺をスタックに積む 8
• 逆辺訪問時 逆 辺 〃 18
• 帰りがけ 分断辺の訪問時に,スタック上にある子孫の辺をまとめて取り出す 12, 13
A B
????
??? C
D E
????
????
F
G H I
A
RRRRRRRRRRRRRRRRRR
B D
C E
F H
RRRRRRRRRRRRRRRRRR
G I
辺を要素とするスタック (最右が最上段)の変化
[ ]
ア−−−−−→[
| | | |]
イ−−−−−→[
|]
ウ−−−−−→[ ]
エ−−−−−→[
| | | | | | |]
オ−−−−−→[
|]
カ−−−−−→[ ]
29