圖論 應用篇

上次寫了篇圖的基本構造方法,運用圖這種強大的數據結構結構,還能解決實際應用中的許多問題,今天這篇就主要整理一些常見的應用

一、路徑問題

路徑問題在圖的處理領域是非常重要的。如我們最常見的走迷宮,就是典型的尋路問題。這里主要運用深度優先和廣度優先算法兩種方式來進行路徑尋找,這2種搜索算法在很多數據結構中都有重要的運用,之前寫的一篇二叉查找樹中的層序遍歷就用到了廣度優先算法,這里就詳細的介紹一下。

1.深度優先尋找路徑

首先是深度優先,為了更加形象,直接看下圖
![][1]

這里以頂點1為出發點,4為終點。假設一個人要走到終點,從1出發有三條路徑,首先沿著往5的路徑進行遍歷,依次1 -> 5 -> 9 -> 8,然后發現沒路了,就返回上一頂點,到頂點5這里發現還有一條路就繼續沿著這條路走,5->3->6,結果又沒路了,就繼續返回到起點,沿著另一條路行走......1->2->4,一看,這下就直接到終點了,轉了幾條路終于找到了終點,那心里真是無比的興奮啊!回到正題,看看這個具體實現,可以用一個boolean類型的變量來標記是否遍歷過該頂點,用一個int型的變量表示從起點到一個頂點的已知路徑上的最后一個頂點。至于圖的基本構造可以參考我之前寫的[圖論基礎篇][2]

/**
 *  圖的深度優先查找路徑
 * @author Legend
 */
public class DepthFirstPaths {

    private boolean[] marked; // 該頂點是否調用過dfs
    private int[] edgeTo; // 從起點到一個頂點的已知路徑上的最后一個頂點
    private final int s; // 起點
    // 圖的初始化
    public DepthFirstPaths(Graph G,int s) {
        marked = new boolean[G.V()];
        edgeTo = new int[G.V()];
        this.s = s;
        dfs(G,s);
    }
    // 深度優先主方法
    private void dfs(Graph G,int v) {
        marked[v] = true;
        // 遍歷與頂點v相連的邊
        for (int w : G.adj(v)) {  
            if (!marked[w]) {
                edgeTo[w] = v;
                dfs(G,w);  // 繼續遞歸的進行遍歷
            }
        }
    }
   // 是否存在s到v的路徑
    public boolean hasPathTo(int v) {
        return marked[v];
    }
   // s到v的路徑
    public Iterable<Integer> PathTo(int v) {
        if (!hasPathTo(v)) {
            return null;
        }
        Stack<Integer> path = new Stack<>();
        for (int i = v;i != s;i = edgeTo[i]) {
            path.push(i);
        }
        path.push(s);
        return path;
    }
}

因為我們要準確的知道每一條路徑,所以這里創建了一個edgeTo變量用于記錄從起點到一個頂點的已知路徑上的最后一個頂點,而edgeTo[w] = v表示的就是從w到v的路徑。再看PathTo方法,首先判斷是否有從s到v的路徑,沒有就返回null。然后實例化一個Stack類型對象path,依次遍歷,把路徑上每一個頂點都push進去,最后在push頂點,并返回path。

2.廣度優先尋找路徑

廣度優先正如其名,優先進行廣度的遍歷,整個過程呈擴散狀。這里還是用上面那張圖,為了方便查看,還是把圖片放到這里。
![][3]
[1]: http://img.mukewang.com/59fb24c20001780108340606.png
[2]: http://blog.cspojie.cn/2017/10/20/%E5%9B%BE%E8%AE%BA-%E5%9F%BA%E7%A1%80%E7%AF%87/#more,這里直接用Graph來表示,然后具體實現看看下面的代碼
[3]: http://img.mukewang.com/59fd753d0001780108340606.png
還是用之前的情景,從頂點1出發,先遍歷和1相鄰的頂點 2,5,3,然后從頂點2開始,右繼續遍歷和2相鄰的頂點,因為已經遍歷過頂點1,所以這里就只需要遍歷頂點7和頂點4,發然后現就直接到終點了,這是在特殊的情況下,如果先遍歷的是另外的頂點,那么幾乎要走完每條路才能找到終點。這里就不多說了,重點講下具體實現,這里用一個抽象數據結構---隊列來實現,首先創建一個隊列,然后把起點標記后插入隊列中去,如果隊列不為空就把當前的頂點彈出隊列,然后依次遍歷和這個頂點相鄰的頂點,并把這些頂點標記后也加入隊列,接下來用同樣的方式將下一頂點彈出隊列,遍歷和其相鄰的頂點,直到隊列為空就停止遍歷。好了,具體看下面的代碼

/**
 *
 * 廣度優先尋找路徑
 * @author Legend
 **/

public class BreadthFirstPaths {

    private boolean[] marked;
    private int[] edgeTo;
    private final int s;
    public BreadthFirstPaths(Graph G,int s) {
        marked = new boolean[G.V()];
        edgeTo = new int[G.V()];
        this.s = s;
        try {
            bfs(G,s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void bfs(Graph G,int s) throws InterruptedException {
        Queue<Integer> queue = new Queue<Integer>();
        marked[s] = true;
        queue.enqueue(s);
        while ( !queue.isEmpty() ) {
            int v = queue.dequeue(); // 從隊列中彈出一個頂點
            for (int w : G.adj(v)) { // 遍歷和該頂點相鄰的頂點
                if ( !marked[w] ) {
                    edgeTo[w] = v; // 保存最短路徑的最后一條邊
                    marked[w] = true; // 標記它 因為最短路徑已知
                    queue.enqueue(w);
                }
            }
        }
    }
    public boolean hasPathTo(int v) {
        return marked[v];
    }
    public Iterable<Integer> pathTo(int v) {
        if ( !hasPathTo(v) ) {
            return null;
        }
        Stack<Integer> path = new Stack<>();
        for (int i = 0;i != s;i =edgeTo[i] ){
            path.push(i);
        }
        path.push(s);
        return path;
    }
}

這里同樣運用了pathTo方法,頂點與路徑的標記過程和深度優先尋找路徑是一樣的,這里就不多說了。

連通分量問題

1.介紹

連通分量也是一個比較常見的問題,主要用于判斷任意兩個結點的連接狀態,特別是用于檢測網絡連接與電路連接的問題中,運用比較廣。

2.基本實現

也沒什么好說的,這里還是運用深度優先的方法,比較簡單,就直接上代碼

/**
 * 使用深度優先找出圖中的連通分量
 *
 * @author Legend
 * @create 2017-11-01 8:23
 **/

public class CC {
    private int count;
    private boolean marked[];
    private int[] id;

    public CC(Graph G) {
        marked = new boolean[G.V()];
        id = new int[G.V()];
        for (int s = 0;s < G.V();s++) {
            if (!marked[s]) {
                dfs(G,s);
                count++;
            }
        }
    }
    private void dfs(Graph G,int v) {
        marked[v] = true;
        id[v] = count;
        for (int w : G.adj(v)) {
            if (!marked[w]) {
                dfs(G,w);
            }
        }
    }
    // 判斷兩個結點是否相連接
    public boolean isConnected(int v,int w) {
        return id[v] == id[w];
    }
    // v所在連通分量的標識符(0~count-1)
    public int id(int v) {
        return id[v];
    }
    // 連通分量的數量
    public int count() {
        return count;
    }
}

雙色問題

1.介紹

能否用2種顏色將圖的所有頂點著色,使得任意一條邊的兩個端點的顏色都不相同,這個問題也就等價于當前的圖是不是二分圖。因為二叉樹其實就是一種比較特殊的圖,所以才有這個問題。

2.基本實現

具體實現可以用一個bool類型的變量來表示2種顏色,直接在構造方法里面進行循環,這里也是運用深度優先的方式。首先判斷當前結點是否被遍歷,沒被遍歷過就進行遍歷,也就是用bool型變量將其標記為true,然后遍歷和這個結點相連的結點,并且把這個結點和相鄰的結點涂上不同的顏色。然后進行判斷
先來看下代碼

/**
 * 雙色問題
 *
 * @author Legend
 * @create 2017-11-01 9:17
 **/

public class TwoColor {
    private boolean[] marked; //當前結點是否被遍歷過
    private boolean[] color; // 表示不同顏色
    private boolean isTwoColorable = true; // 是否能用2種顏色表示
    public TwoColor(Graph G) {
        marked = new boolean[G.V()];
        color = new boolean[G.V()];
        for (int s = 0;s < G.V();s++) {
            if (!marked[s]) {
                dfs(G,s);
            }
        }
    }
    private void dfs(Graph G,int v) {
        marked[v] = true;
        for (int w : G.adj(v)) {
            if (!marked[w]) {
                color[w] = !color[v];
                dfs(G,w);
            } else if (color[w] == color[v]) {
                isTwoColorable = false;
            }
        }
    }
    public boolean isBipartite() {
        return isTwoColorable;
    }
}

如果這2個頂點顏色相同,則不能使得任意一條邊的2個端點的顏色都不相同,則這個圖不是二分圖,試想一下,如果是二分圖,任意一條的兩個端點肯定顏色是不相同的。因為每次遍歷時都將當前頂點與連接頂點標記了2種不同的顏色,如果這個頂點有多個相鄰頂點,并且這些相鄰頂點又有邊相連,這必然會造成2個顏色相同,這樣的圖自然也不可能是二分圖了。

因為這篇博客主要是整理圖論的一些應用的問題,所以對于這些問題的優化這里不是重點,有興趣的可以自己去查查資料,那圖論應用篇就暫時到這里了。
ps:該blog首發lenged's blog

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容

  • 第一章 緒論 什么是數據結構? 數據結構的定義:數據結構是相互之間存在一種或多種特定關系的數據元素的集合。 第二章...
    SeanCheney閱讀 5,803評論 0 19
  • 本文涉及更多的是概念,代碼部分請參考之前寫過的 2 篇博客 基于Javascript的排序算法基本數據結構和查找算...
    faremax閱讀 1,264評論 0 2
  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個結點至多有m個孩子。 除根結點和葉子結點外,其它每個結點至少有m...
    文檔隨手記閱讀 13,286評論 0 25
  • 圖是一種比線性表和樹更復雜的數據結構,在圖中,結點之間的關系是任意的,任意兩個數據元素之間都可能相關。圖是一種多對...
    Alent閱讀 2,330評論 1 22
  • 在不同人眼里,人生有不同的狀態。樂觀的人把人生活成一場喜劇,悲觀的人把人生活成一場悲劇。你很堅強,那么你就可以支配...
    怪咖習心閱讀 376評論 0 0