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