アルゴリズムとデータ構造 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
© Copyright 2024 Paperzz