アルゴリズムとデータ構造 2012年7月12日 酒居敬一([email protected]) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2012/index.html 1 各種の連結性の判定 (249ページ) • 連結の強さを表現する概念 • 無向グラフ – 二重連結 – 二重連結成分 • 有向グラフ – 強連結 – 強連結成分 2 二重連結 (250ページ) F A 図4.4.1 二重連結グラフ グラフから1つの頂点と その頂点を端点とする すべての辺を除いて得られる グラフを考えたとき、どの頂点を 除いた場合でもグラフが連結 グラフであるとき、元のグラフは 二重連結であるという。 D G C E H B 図4.4.2 関節点と二重連結成分 I •二重連結成分とは、二重 連結な部分グラフのこと。 •取り除くと非連結になる 頂点を関節点という。 3 関節点検出のアルゴリズム (251ページ) • 前提条件 – 関節点が検出されなければ、二重連結。 – 深さ優先探索の結果の木を仮定して、 子孫・祖先・部分木という言葉を使う。 • 場合分け – 頂点が木の根の場合 • 子が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を経由 せずにBとCが連絡できる。 •BとCがAを通らないと連絡できない のは、どちらかの部分木にAより上の 祖先を指す逆辺がないからであり、 Aが関節点になっている。 A B C 6 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); 根の子が2個以上(順序数が2より大きい) boolean artic; ときは、根が関節点。 if (node.getSequence() == 1) { artic = (neighbor.getSequence() != 2); } else { 自分(node)の子から、祖先への逆辺 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); 深く探索していく過程で、より祖先寄りの祖先への逆辺を } 調べながら進んでいる。 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); 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(nodeI); new Biconnected<Character>().search(nodeA); } D G C B I 図4.4.2 頂点Dは関節点 頂点Eは関節点 頂点Eは関節点 頂点Cは関節点 8 強連結 (255ページ) 部分グラフのうち、強連結であって、 しかもそれ以上頂点を追加すると 強連結でなくなるものを強連結成分という。 図4.4.5 強連結グラフ 有向グラフの各頂点から任意の 頂点へ至る道が存在するとき、 この有向グラフを強連結であるという。 9 図4.4.6 強連結成分への分割 強連結成分への分割アルゴリズム (256ページ) 有向グラフの深さ優先探索の応用である。 下降辺は強連結性に貢献しない。 下降辺の両端の頂点について、祖先から子孫へ 木の辺から成る道がすでに存在する。 上昇辺の両端の頂点は同じ強連結成分に属す。 木の辺と上昇辺で閉路を作る。 木の辺で下降し、上昇辺で上昇して元に戻れる。 交差辺は、単純ではない。 強連結成分に属す・属さないの2種類がある。 10 •深さ優先探索の木の上で強連結成分を表す。 •実際は下降辺・上昇辺・交差辺も存在してて、 それらとともに強連結成分を作っている。 •各強連結成分に最初に訪問した頂点 =その部分木の根。 •部分木の根からその子孫に到達可能。 •部分木が強連結成分なら、 木の辺を0回以上通った後で 上昇辺か交差辺を通って根に戻れる。 •深さ優先探索の木の葉から、上昇辺 や交差辺を通ってその葉の祖先に たどり着くことができ、そういう祖先の うちで最も根に近いものを求める。 •教科書のlowlink変数 強連結成分を見つけたら、 この辺を木からはずす。 11 図4.4.7 深さ優先探索の木と強連結成分の関係 (木の辺だけを見たときは木) public class StrongComponent<E> { private int sequence; 訪問順seq private Stack<Node<E>> stack = new Stack<Node<E>>(); private int visit(Node<E> node){ 教科書のlowlink 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(); (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(); } nodeA.connectTo(nodeB); nodeA.connectTo(nodeC); nodeB.connectTo(nodeE); nodeB.connectTo(nodeF); nodeC.connectTo(nodeF); 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); 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 (いつものA107は使えません) • 日時: 2012年7月30日16時30分~18時00分 – 入室限度: 16時50分まで – 退出可能: 17時00分より • 持ち込み可 – 教科書・資料(自筆・コピー問わず)は持ち込み可 – 人間・パソコン・携帯電話・PHSなど持ち込み不可 • 学生証必携 – 持っていない場合は教務で発行してもらうこと 15
© Copyright 2024 ExpyDoc