アルゴリズムとデータ構造

アルゴリズムとデータ構造
2011年
2011
年7月7日
酒居敬一(sakai.keiichi@kochi
sakai.keiichi@kochi--tech.ac.jp)
tech.ac.jp)
http://www.info.kochihttp://www info kochihttp://www.info.kochi
kochi-tech.ac.jp/k1sakai/Lecture/ALG/2011/index.html
tech ac jp/k1sakai/Lecture/ALG/2011/index html
1
各種の連結性の判定
(249ページ)
• 連結の強さを表現する概念
• 無向グラフ
無向グラ
– 二重連結
– 二重連結成分
• 有向グラフ
– 強連結
– 強連結成分
2
二重連結
(250ページ)
F
A
図4.4.1 二重連結グラフ
グ
グラフから1つの頂点と
その頂点を端点とする
すべての辺を除いて得られる
グラフを考えたとき、どの頂点を
除いた場合でもグラフが連結
グラフであるとき、元のグラフは
二重連結であるという。
D
G
C
E
H
B
図4.4.2 関節点と二重連結成分
I
•二重連結成分とは、二重
連結な部分グラフのこと。
連結な部分グラフのこと
•取り除くと非連結になる
頂点を関節点という。
3
関節点検出のアルゴリズム
(251ペ ジ)
(251ページ)
• 前提条件
– 関節点が検出されなければ、二重連結。
– 深さ優先探索の結果の木を仮定して、
深さ優先探索の結果の木を仮定して
子孫・祖先・部分木という言葉を使う。
• 場合分け
場合分
– 頂点が木の根の場合
• 子が2個以上ならその頂点は関節点である。
子が2個以上ならその頂点は関節点である
– 頂点が木の根ではない場合(次のページ)
• 着目する3頂点の位置で2つに場合分けする。
着目する3頂点の位置で2つに場合分けする
– いずれもAB間・BC間・CA間の道について考える。
4
BはAの祖先、CはAの子孫、
という位置関係の場合
という位置関係の場合。
(図 4.4.3 a)
B
•もしAが関節点ならば、
以下の2つは同じことを言っている
以
同
を言
1. BC間の道が必ずAを通ること
2. Cを含む部分木の中にAの祖先への
逆辺がな
逆辺がないこと
と
•図のように逆辺 (点線で表示)があれば、
(点線で表示)があれば
Aを経由せずにBC間を移動できるので、
この場合はAは関節点ではない。
A
C
5
BはAの子孫、CもAの子孫、
という位置関係の場合。
という位置関係の場合
(図 4.4.3 b)
•図のように、BとCの双方が、Aの祖先と
Aを経由せずに連絡できれば Aは
Aを経由せずに連絡できれば、Aは
関節点ではない。この場合、Aを経由
せずにBとCが連絡できる。
•BとCがAを通らないと連絡できない
のは どちらかの部分木にAより上の
のは、どちらかの部分木にAより上の
祖先を指す逆辺がないからであり、
Aが関節点になっている。
A
B
C
6
public class Biconnected<E> {
private int sequence;
private int visit(Node<E> node){
node.setSequence(++this.sequence);
d s tS
( this s
)
隣接する未訪問の頂点に関しては、
int min = this.sequence;
深さ優先探索なので探索する。
for(Node<E> neighbor: node.getEdges()){
その結果、訪問順が付けられる。
if(neighbor.getSequence()
f( g
g
q
() == 0){
){
int m = visit(neighbor);
min = Math.min(min, m);
根の子が2個以上(順序数が2より大きい)
boolean artic;
ときは、根が関節点。
if (node.getSequence()
(n d
tS qu nc () == 1) {
artic = (neighbor.getSequence() != 2);
} else {
自分(node)の子から、祖先への逆辺
artic = ((m >= node.getSequence());
g
q
())
がなければ 自分は関節点
がなければ、自分は関節点。
}
if(artic) System.out.println("頂点" + node.getValue() + "は関節点");
} else if (neighbor.getSequence() < min){
min = neighbor.getSequence();
neighbor getSequence();
}
隣接する訪問済みの頂点に関しては、
}
より祖先へ向かう辺(逆辺含む)の先の
return min;
頂点の順序数を保持する。
点
序数を保持す
}
public void search(Node<E> start){
this.sequence = 0;
visit(start);
深く探索していく過程で より祖先寄りの祖先 の逆辺を
深く探索していく過程で、より祖先寄りの祖先への逆辺を
}
調べながら進んでいる。
7
}
F
A
public static void main(String[] args) {
System.out.println("図4.4.2");
for(Node<Character> node: test_data2){
node.reset();
}
nodeA.connect(nodeC);
nodeA connect(nodeD);
nodeA.connect(nodeD);
E
nodeB.connect(nodeC);
nodeB.connect(nodeD);
nodeC.connect(nodeE);
H
nodeD.connect(nodeF);
nodeD.connect(nodeG);
nodeF.connect(nodeG);
nodeE connect(nodeH);
nodeE.connect(nodeH);
nodeE.connect(nodeI);
new Biconnected<Character>().search(nodeA);
}
D
G
C
B
I
図4.4.2
図4
4 2
頂点Dは関節点
頂点Eは関節点
頂点Eは関節点
頂点Cは関節点
8
強連結
(255ページ)
部分グラフのうち、強連結であって、
部分グラフのうち
強連結であって
しかもそれ以上頂点を追加すると
強連結でなくなるものを強連結成分という。
図4.4.5 強連結グラフ
強
有向グラフの各頂点から任意の
頂点へ至る道が存在するとき、
この有向グラフを強連結であるという。
9
図4.4.6 強連結成分への分割
強連結成分への分割アルゴリズム
(256ページ)
9有向グラフの深さ優先探索の応用である。
9下降辺は強連結性に貢献しない。
9下降辺の両端の頂点について、祖先から子孫へ
9下降辺の両端の頂点について
祖先から子孫
木の辺から成る道がすでに存在する。
9上昇辺の両端の頂点は同じ強連結成分に属す。
9上昇辺の両端の頂点は同じ強連結成分に属す
9木の辺と上昇辺で閉路を作る。
9 木の辺で下降し、上昇辺で上昇して元に戻れる。
木の辺で下降し 上昇辺で上昇して元に戻れる
9交差辺は、単純ではない。
9強連結成分に属す・属さないの2種類がある。
強連結成
す
な
種類があ
10
•深さ優先探索の木の上で強連結成分を表す。
•実際は下降辺・上昇辺・交差辺も存在してて、
それらとともに強連結成分を作っている。
•各強連結成分に最初に訪問した頂点
=その部分木の根。
=その部分木の根
•部分木の根からその子孫に到達可能。
•部分木が強連結成分なら、
木の辺を0回以上通 た後で
木の辺を0回以上通った後で
上昇辺か交差辺を通って根に戻れる。
•深さ優先探索の木の葉から、上昇辺
や交差辺を通ってその葉の祖先に
たどり着くことができ そういう祖先の
たどり着くことができ、そういう祖先の
うちで最も根に近いものを求める。
•教科書のlowlink変数
教科書のlowlink変数
強連結成分を見つけたら、
この辺を木からはずす。
11
図4.4.7 深さ優先探索の木と強連結成分の関係
(木の辺だけを見たときは木)
public class StrongComponent<E> {
private int sequence;
訪問順seq
private Stack<Node<E>> stack = new Stack<Node<E>>();
private
i t int
i t visit(Node<E>
isit(N d E node){
d ){
教科書のlowlink
node.setSequence(++this.sequence);
int min = this.sequence;
stack.push(node);
p
(
);
未訪問の場合は探索を進める。
for(Node<E> neighbor: node.getEdges()){
int m;
if(neighbor.getSequence() == 0) m = visit(neighbor);
else
ls m = neighbor.getSequence();
n i hb
tS qu nc ();
隣の頂点が訪問済みの場合は、
min = Math.min(min, m);
そこからたどれる最も根に
}
近い頂点の訪問順を更新する。
if(min
(
== node.getSequence()){
g
q
()){
Node<E> component;
訪問済みなので木の辺じゃない。
do {
component = stack.pop();
System out print(component getValue() + " ");
System.out.print(component.getValue()
深さ優先探索し終わ た後
深さ優先探索し終わった後、
component.setSequence(Integer.MAX_VALUE);
調べてみたら、
} while (component != node);
強連結成分の根だった
System.out.println();
y
p
()
(seq=lowlink)
}
return min;
交差辺経由で探索されないようにする。
この根からつながる
}
強連結成分を取り出す
強連結成分を取り出す。
public void search(Collection<Node<E>> graph){
this.sequence = 0; this.stack.clear();
12
for(Node<E> node: graph) if(node.getSequence() == 0) visit(node);
}}
public static void main(String[] args) {
System.out.println("図4.4.9");
for(Node<Character> node: test_data2){
node.reset();
d
s t()
}
nodeA.connectTo(nodeB);
nodeA.connectTo(nodeC);
(
);
nodeB.connectTo(nodeE);
nodeB.connectTo(nodeF);
nodeC.connectTo(nodeF);
n d C c nn ctT (n d G);
nodeC.connectTo(nodeG);
nodeD.connectTo(nodeA);
nodeE.connectTo(nodeD);
D
nodeE.connectTo(nodeH);
(
)
nodeF.connectTo(nodeE);
nodeG.connectTo(nodeH);
nodeH.connectTo(nodeI);
nodeI connectTo(nodeH);
nodeI.connectTo(nodeH);
new StrongComponent<Character>().search(test_data2);
}
図4.4.9
I H
G
C F D E B A
A
B
E
C
F
G
H
I
図 4.4.9 プログラムの実行例
(実線は木の辺 点線はそれ以外の辺)
(実線は木の辺、点線はそれ以外の辺)
13
擬似コード
擬似コ
ド
(Pseudocode)
• 擬似プログラムともいう。
• アルゴリズムを既存のプログラミング言語に似
ゴ ズムを既存 プ グ
グ言語 似
せて書いたもの。
– かつてはPascalやFortran、今はJava?
• 要点だけ書くのでそのまま動く保障はない。
要点だけ書くのでそのまま動く保障はない
– 教科書のコードはかなり完成度が高い。
– Javaだと、どこでも動くコードを書きやすい。
14
期末試験
• 教室: C101
• 日時: 2011年7月25日16時30分∼18時00分
– 入室限度: 16時50分まで
– 退出可能: 17時00分より
• 持ち込み可
– 教科書・資料(自筆・コピー問わず)は持ち込み可
教科書 資料(自筆 コピ 問わず)は持ち込み可
– 人間・パソコン・携帯電話・PHSなど持ち込み不可
• 学生証必携
– 持っていない場合は教務で発行してもらうこと
15