成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

算法(第4版) Chapter 4.2 有向圖

曹金海 / 1802人閱讀

摘要:只好特地拎出來(lái)記錄證明一下算法步驟第一步在逆圖上運(yùn)行,將頂點(diǎn)按照逆后序方式壓入棧中顯然,這個(gè)過(guò)程作用在有向無(wú)環(huán)圖上得到的就是一個(gè)拓?fù)渑判蜃饔迷诜巧系玫降氖且粋€(gè)偽拓?fù)渑判虻诙皆谠瓐D上按第一步的編號(hào)順序進(jìn)行。等價(jià)于已知在逆圖中存在有向路徑。

Algorithms Fourth Edition
Written By Robert Sedgewick & Kevin Wayne
Translated By 謝路云
Chapter 4 Section 2 有向圖

有向圖的建立 有向圖API

修改了方法void addEdge(v, w) 添加的邊為單向的, 從v到w

修改了方法adj(v) 返回的是從v指出去的邊連接的頂點(diǎn)

增加了方法Digraph reverse() 創(chuàng)建了一個(gè)反向邊的圖副本,因?yàn)橛袝r(shí)候我們需要的是指向特定頂點(diǎn)的其他頂點(diǎn)

Digraph 代碼
public class Digraph {
    private final int V;
    private int E;
    private Bag[] adj;

    public Digraph(int V) {
        this.V = V;
        this.E = 0;
        adj = (Bag[]) new Bag[V];
        for (int v = 0; v < V; v++)
            adj[v] = new Bag();
    }

    public int V() {
        return V;
    }

    public int E() {
        return E;
    }

    public void addEdge(int v, int w) {
        adj[v].add(w);
        E++;
    }

    public Iterable adj(int v) {
        return adj[v];
    }

    public Digraph reverse() {
        Digraph R = new Digraph(V);
        for (int v = 0; v < V; v++)
            for (int w : adj(v))
                R.addEdge(w, v);
        return R;
    }
}
可達(dá)性(深度優(yōu)先搜索) 可達(dá)性API

增加了構(gòu)造函數(shù)DirectedDFS(G,sources) 一個(gè)source生成一棵樹(shù),n個(gè)sources生成好n棵樹(shù);也有可能是一棵樹(shù),只是先找到了孫子,沒(méi)法通過(guò)孫子找爸爸和爺爺,后來(lái)輸入了爺爺,找到了爸爸,連到了孫子,形成了一棵大樹(shù)。

DirectedDFS 代碼

基本和有向圖沒(méi)區(qū)別

public class DirectedDFS {
    private boolean[] marked;

    public DirectedDFS(Digraph G, int s) {
        marked = new boolean[G.V()];
        dfs(G, s);
    }

    public DirectedDFS(Digraph G, Iterable sources) {
        marked = new boolean[G.V()];
        for (int s : sources)
            if (!marked[s]) dfs(G, s);
    }

    private void dfs(Digraph G, int v) {
        marked[v] = true;
        for (int w : G.adj(v))
            if (!marked[w]) dfs(G, w);
    }

    public boolean marked(int v) {
        return marked[v];
    }
}
多點(diǎn)可達(dá)性應(yīng)用

標(biāo)記-清除的垃圾箱

尋路

環(huán)和有向無(wú)環(huán)圖

調(diào)度問(wèn)題

拓?fù)渑判?給定一副有向圖,將所有頂點(diǎn)排序,使得所有的有向邊從排在前面的元素指向后面的元素

有向無(wú)環(huán)圖 Directed Acyclic Graph (DAG)

有向環(huán)API

方法boolean hasCycle() 有向環(huán)檢測(cè)

DirectedCycle 代碼
public class DirectedCycle {
    private boolean[] marked;
    private int[] edgeTo;
    private Stack cycle; // vertices on a cycle (if one exists)
    private boolean[] onStack; // vertices on recursive call stack

    public DirectedCycle(Digraph G) {
        onStack = new boolean[G.V()];
        edgeTo = new int[G.V()];
        marked = new boolean[G.V()];
        for (int v = 0; v < G.V(); v++)
            if (!marked[v]) dfs(G, v);
    }

    private void dfs(Digraph G, int v) {
        onStack[v] = true; //這個(gè)變量是神來(lái)之筆。因?yàn)橛泻脦卓脴?shù),但我只要查我所在的這棵樹(shù)。所以在遞歸的時(shí)候一路把這棵樹(shù)標(biāo)成true,在返回之前再標(biāo)回false。為下一棵樹(shù)可以循環(huán)再利用做準(zhǔn)備。
        marked[v] = true;
        for (int w : G.adj(v))
            if (this.hasCycle()) return;
            else if (!marked[w]) {
                edgeTo[w] = v;
                dfs(G, w);
            } else if (onStack[w]) { // 我遇到了組織,我們形成了一個(gè)環(huán)!
                cycle = new Stack();
                for (int x = v; x != w; x = edgeTo[x])
                    cycle.push(x);
                cycle.push(w);
                cycle.push(v);//v壓了兩次,第一次作為箭頭終點(diǎn),第二次作為箭頭起點(diǎn)
            }
        onStack[v] = false;
    }

    public boolean hasCycle() {
        return cycle != null;
    }

    public Iterable cycle() {
        return cycle;
    }
}
拓?fù)渑判駻PI

前提

當(dāng)且僅當(dāng)一幅圖是有向無(wú)環(huán)圖時(shí),才能進(jìn)行拓?fù)渑判?/strong>

一種拓?fù)渑判虻膶?shí)現(xiàn)方式

深度優(yōu)先搜索DFS

頂點(diǎn)排列順序

前序 遞歸調(diào)用之前將頂點(diǎn)加入隊(duì)列

后續(xù) 遞歸調(diào)用之后將頂點(diǎn)加入隊(duì)列

逆后續(xù) 遞歸調(diào)用之后將頂點(diǎn)壓入棧

DepthFirstOrder 代碼

頂點(diǎn)排列順序

就是記錄頂點(diǎn)的位置和方式不一樣

public class DepthFirstOrder {
    private boolean[] marked;
    private Queue pre; // vertices in preorder
    private Queue post; // vertices in postorder
    private Stack reversePost; // vertices in reverse postorder

    public DepthFirstOrder(Digraph G) {
        pre = new Queue();
        post = new Queue();
        reversePost = new Stack();
        marked = new boolean[G.V()];
        for (int v = 0; v < G.V(); v++)
            if (!marked[v]) dfs(G, v);
    }

    private void dfs(Digraph G, int v) {
        pre.enqueue(v);
        marked[v] = true;
        for (int w : G.adj(v))
            if (!marked[w]) dfs(G, w);
        post.enqueue(v);
        reversePost.push(v);
    }

    public Iterable pre() {
        return pre;
    }

    public Iterable post() {
        return post;
    }

    public Iterable reversePost() {
        return reversePost;
    }
}
Topological 代碼

復(fù)雜度:

時(shí)間: V+E

(為什么我覺(jué)得Topological這個(gè)類(lèi)沒(méi)干什么實(shí)事,DirectedCycle檢測(cè)是否是有向無(wú)環(huán)圖,DepthFirstOrder進(jìn)行了排序。。。Topological感覺(jué)就封裝了一下)

public class Topological {
    private Iterable order; // topological order

    public Topological(Digraph G) {
        DirectedCycle cyclefinder = new DirectedCycle(G);
        if (!cyclefinder.hasCycle()) {
            DepthFirstOrder dfs = new DepthFirstOrder(G);
            order = dfs.reversePost(); //排序方式
        }
    }

    public Iterable order() {
        return order;
    }

    public boolean isDAG() {
        return order == null;
    }
}
強(qiáng)聯(lián)通性

定義

w和v是相互可達(dá)的,則稱(chēng)它們?yōu)閺?qiáng)連通的(Strongly Connected)
(v到w有一條路徑,則w是從v可達(dá)的)

如果有向圖G的每?jī)蓚€(gè)頂點(diǎn)都強(qiáng)連通,稱(chēng)G是一個(gè)強(qiáng)連通圖。

有向圖的極大強(qiáng)連通子圖,稱(chēng)為強(qiáng)連通分量(Strongly Connected Components/Strong Components)

兩個(gè)頂點(diǎn)是強(qiáng)連通的,當(dāng)且僅當(dāng)它們?cè)谕粋€(gè)有向環(huán)中

性質(zhì)

自反性

對(duì)稱(chēng)性

傳遞性

強(qiáng)連通分量API

Strongly Connected Components

Kosaraju算法

算法步驟很簡(jiǎn)單,但是原理很玄幻。。。只好特地拎出來(lái)記錄+證明一下

算法步驟

第一步:在逆圖GR上運(yùn)行DFS,將頂點(diǎn)按照逆后序(reversePost)方式壓入棧Stack s中
(顯然,這個(gè)過(guò)程作用在有向無(wú)環(huán)圖DAG上得到的就是一個(gè)拓?fù)渑判?;作用在非DAG上得到的是一個(gè)偽拓?fù)渑判颍?/p>

第二步:在原圖G上按第一步s.pop()的編號(hào)順序進(jìn)行DFS。
(棧的特點(diǎn)是FILO,先進(jìn)后出)

算法基于CC(ConnectedComponents)

CC按順序0~G.V()

SCC按順序s.pop()

SCC順序怎么得到?

類(lèi)DepthFirstOrder

兩次DFS

圖示兩次DFS

KosarajuSCC 代碼

復(fù)雜度:所需時(shí)間與空間與V+E成正比,連通性查詢(xún)?yōu)槌?shù)時(shí)間

時(shí)間: 處理有向圖的反向圖,+ 2次DFS 這三步所需的時(shí)間都與V+E成正比

空間: 反向復(fù)制一副有向圖的空間與V+E成正比

連通性查詢(xún): 數(shù)組id[]查詢(xún),常數(shù)時(shí)間

public class KosarajuSCC {
    private boolean[] marked; // reached vertices
    private int[] id; // component identifiers
    private int count; // number of strong components

    public KosarajuSCC(Digraph G) {
        marked = new boolean[G.V()];
        id = new int[G.V()];
        DepthFirstOrder order = new DepthFirstOrder(G.reverse());
        for (int s : order.reversePost())
            if (!marked[s]) {
                dfs(G, s);
                count++;
            }
    }

    private void dfs(Digraph G, int v) {
        marked[v] = true;
        id[v] = count;
        for (int w : G.adj(v))
            if (!marked[w]) dfs(G, w);
    }

    public boolean stronglyConnected(int v, int w) {
        return id[v] == id[w];
    }

    public int id(int v) {
        return id[v];
    }

    public int count() { //復(fù)制書(shū)上的,感覺(jué)返回的不是強(qiáng)連通的個(gè)數(shù)N,因?yàn)閺牧汩_(kāi)始計(jì)數(shù),所以返回的是N-1
        return count;
    }
}
模糊猜想

原圖

pop順序   →→→→     →→→→→→→→→
7 → 8 6 → 9 → 11 10 → 12 0 → 5 → 4 → 3 → 2 1
 ←     ←←←←←←   ←←←←←←←      

由于排版問(wèn)題只畫(huà)了部分的有向邊

可見(jiàn)是否是環(huán),應(yīng)該從1開(kāi)始排除,因?yàn)椋笔菆D中最深的結(jié)點(diǎn),最有可能只有指向1的,而沒(méi)有從1指出去的。通過(guò)開(kāi)始一個(gè)一個(gè)排查,應(yīng)該是一種比較好的想法。

算法正確性證明

證明的目標(biāo),就是最后一步 --- 每一顆搜索樹(shù)代表的就是一個(gè)強(qiáng)連通分量

首先 最后一步是在原圖G中通過(guò)s找到其他頂點(diǎn)v的,即從s→v是可達(dá)的。
那么我們需要證明,原圖G中v→s也是可達(dá)的。

等價(jià)于已知:在逆圖GR中存在有向路徑v→s。
那么要證明:逆圖GR中從s→v是可達(dá)的。

而之所以DFS(s)能夠在DFS(v)之前被調(diào)用,是因?yàn)樵趯?duì)G獲取ReversePost-Order序列時(shí),s出現(xiàn)在v之前,這也就意味著,v是在s之前加入該序列的(因?yàn)樵撔蛄惺褂脳W鳛閿?shù)據(jù)結(jié)構(gòu),先加入的反而會(huì)在序列的后面)。

因此根據(jù)DFS調(diào)用的遞歸性質(zhì),DFS(v)應(yīng)該在DFS(s)之前返回,而有當(dāng)時(shí)在逆圖GR中兩種情形滿(mǎn)足該條件:
DFS(v) START -> DFS(v) END -> DFS(s) START -> DFS(s) END
DFS(s) START -> DFS(v) START -> DFS(v) END -> DFS(s) END

第一種情形下,調(diào)用DFS(v)卻沒(méi)能在它返回前遞歸調(diào)用DFS(s),與在逆圖GR中存在有向路徑v→s相矛盾的,因此不可取。
故情形二為唯一符合邏輯的調(diào)用過(guò)程。而根據(jù)DFS(s) START -> DFS(v) START可以推導(dǎo)出在逆圖GR中存在有向路徑s→v。

所以從s到v以及v到s都有路徑可達(dá),證明完畢。

頂點(diǎn)對(duì)的可達(dá)性

頂點(diǎn)對(duì)的四種情況

w?v 強(qiáng)連通
w→v
w←v
w?v

問(wèn)題:如何寫(xiě)一個(gè)算法判斷從w到v是可達(dá)的嗎?
有向圖的可達(dá)性問(wèn)題 和 連通性 有很大區(qū)別。 因?yàn)檫B通性是雙向的,可達(dá)性是單向的。

目前的解決辦法:傳遞閉包(其實(shí)就是一個(gè)類(lèi)似于鄰接矩陣的矩陣,用來(lái)記錄是否連通)

傳遞閉包API

TransitiveClosure 代碼

給每個(gè)頂點(diǎn)創(chuàng)立了一棵樹(shù),在每棵樹(shù)里有數(shù)組marked[V],標(biāo)記是否連通。

復(fù)雜度

空間:V*V

時(shí)間:V*(V+E)

public class TransitiveClosure {
    private DirectedDFS[] all;

    TransitiveClosure(Digraph G) {
        all = new DirectedDFS[G.V()];
        for (int v = 0; v < G.V(); v++)
            all[v] = new DirectedDFS(G, v);
    }

    boolean reachable(int v, int w) {
        return all[v].marked(w);
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66497.html

相關(guān)文章

  • 算法4Chapter 4.2 強(qiáng)聯(lián)通性 Tarjan算法補(bǔ)充

    摘要:在實(shí)際的測(cè)試中,算法的運(yùn)行效率也比算法高左右。此外,該算法與求無(wú)向圖的雙連通分量割點(diǎn)橋的算法也有著很深的聯(lián)系。學(xué)習(xí)該算法,也有助于深入理解求雙連通分量的算法,兩者可以類(lèi)比組合理解。固屬于同一連通分支。 參考資料http://blog.csdn.net/acmmmm/a...https://www.byvoid.com/blog/s...http://blog.csdn.net/noth...

    maybe_009 評(píng)論0 收藏0
  • 算法4Chapter 4.4 最短路徑

    摘要:相關(guān)操作就是判斷的不等號(hào)符號(hào)改反,初始值設(shè)為負(fù)無(wú)窮副本的最短路徑即為原圖的最長(zhǎng)路徑。方法是同上面一樣構(gòu)造圖,同時(shí)會(huì)添加負(fù)權(quán)重邊,再將所有邊取反,然后求最短路徑最短路徑存在則可行沒(méi)有負(fù)權(quán)重環(huán)就是可行的調(diào)度。 Algorithms Fourth EditionWritten By Robert Sedgewick & Kevin WayneTranslated By 謝路云Chapter ...

    leap_frog 評(píng)論0 收藏0
  • 算法4Chapter 5.1 字符串排序

    摘要:區(qū)別把數(shù)字對(duì)應(yīng)成字符。這個(gè)是字符串的第位。稍作修改可適應(yīng)不等長(zhǎng)的字符串。因此增加一個(gè)組別,記錄字符為空的頻次。 Algorithms Fourth EditionWritten By Robert Sedgewick & Kevin WayneTranslated By 謝路云Chapter 5 Section 1 字符串排序 參考資料http://blog.csdn.net/gua...

    Amio 評(píng)論0 收藏0
  • 算法4Chapter 1

    摘要:由的位置決定乘以幾,依次為,,,,,。遞歸算法遞歸算法就是本次結(jié)果用另外的參數(shù)調(diào)用自己。然而這個(gè)函數(shù)方法本身并沒(méi)有用,因?yàn)榉椒ㄖ腥魝鬟f參數(shù)為基本型如,在方法中對(duì)其值的改變并不會(huì)在主函數(shù)中產(chǎn)生影響。 Algorithms Fourth EditionWritten By Robert Sedgewick & Kevin WayneTranslated By 謝路云 筆記 二分查找 Bin...

    Jacendfeng 評(píng)論0 收藏0
  • 算法4Chapter 2.4 優(yōu)先隊(duì)列

    摘要:堆中位置的結(jié)點(diǎn)的父節(jié)點(diǎn)的位置為,子節(jié)點(diǎn)的位置分別是和一個(gè)結(jié)論一棵大小為的完全二叉樹(shù)的高度為用數(shù)組堆實(shí)現(xiàn)的完全二叉樹(shù)是很?chē)?yán)格的,但它的靈活性足以使我們高效地實(shí)現(xiàn)優(yōu)先隊(duì)列。 Algorithms Fourth EditionWritten By Robert Sedgewick & Kevin WayneTranslated By 謝路云Chapter 2 Section 4 優(yōu)先隊(duì)列 ...

    Turbo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<