學習總結(jié)(數(shù)據(jù)結(jié)構(gòu):圖論)

轉(zhuǎn)載請標明出處,謝謝!
http://www.itdecent.cn/p/cf03e51a3ca2

關(guān)聯(lián)文章
冒泡、選擇排序 http://www.itdecent.cn/p/176b0b892591
棧和隊列 http://www.itdecent.cn/p/8cb602ef4e21
順序表、單雙鏈表 http://www.itdecent.cn/p/3aeb5998e79e
二叉樹 http://www.itdecent.cn/p/de829eab944c
圖論 http://www.itdecent.cn/p/cf03e51a3ca2

定義:圖(graph)是由一些點(vertex)和這些點之間的連線(edge)所組成的;其中,點通常被成為”頂點(vertex)”,而點與點之間的連線則被成為”邊或弧”(edege)。通常記為,G=(V,E)。

無向圖

image.png

有向圖

image.png

鄰接點
一條邊上的兩個頂點叫做鄰接點。 例如無向圖中的A點的B點就是鄰接點

在有向圖中,除了鄰接點之外;還有”入邊”和”出邊”的概念。
頂點的入邊,是指以該頂點為終點的邊。而頂點的出邊,則是指以該頂點為起點的邊。


在無向圖中,某個頂點的度是鄰接到該頂點的邊(或弧)的數(shù)目。
例如,上面無向圖中頂點A的度是3。

在有向圖中,度還有”入度”和”出度”之分。
某個頂點的入度,是指以該頂點為終點的邊的數(shù)目。而頂點的出度,則是指以該頂點為起點的邊的數(shù)目。
頂點的度=入度+出度。
例如,上面有向圖G2中,頂點B的入度是2,出度是2;頂點B的度=2+2=4。

路徑和回路
路徑:如果頂點(Vm)到頂點(Vn)之間存在一個頂點序列。則表示Vm到Vn是一條路徑。
路徑長度:路徑中”邊的數(shù)量”。
簡單路徑:若一條路徑上頂點不重復出現(xiàn),則是簡單路徑。
回路:若路徑的第一個頂點和最后一個頂點相同,則是回路。
簡單回路:第一個頂點和最后一個頂點相同,其它各頂點都不重復的回路則是簡單回路。

連通圖和連通分量
連通圖:對無向圖而言,任意兩個頂點之間都存在一條無向路徑,則稱該無向圖為連通圖。 對有向圖而言,若圖中任意兩個頂點之間都存在一條有向路徑,則稱該有向圖為強連通圖。

連通分量:非連通圖中的各個連通子圖稱為該圖的連通分量。

權(quán)

image.png

在某些圖中,邊具有與之相關(guān)的數(shù)值,稱為權(quán)

鄰接矩陣
用一個一維數(shù)組存放圖中所有頂點數(shù)據(jù);用一個二維數(shù)組存放頂點間關(guān)系(邊或?。┑臄?shù)據(jù),這個二維數(shù)組稱為鄰接矩陣。
無向圖

image.png

有向圖


image.png

帶權(quán)有向圖


image.png

鄰接表
鄰接表存儲的基本思想:對于圖的每個頂點vi,將所有鄰接于vi的頂點鏈成一個單鏈表,稱為頂點vi的邊表(對于有向圖則稱為出邊表),所有邊表的頭指針和存儲頂點信息的一維數(shù)組構(gòu)成了頂點表。

將節(jié)點存入數(shù)組中,并對節(jié)點的孩子進行鏈式存儲,不管有多少孩子,也不會存在空間按浪費的問題,這個思路同樣適用于圖的存儲。我們把這種數(shù)組與鏈表相結(jié)合的存儲方法稱為鄰接表

無向圖

image.png

有向圖

image.png

帶權(quán)

image.png

圖的遍歷
[參考:](https://blog.csdn.net/zhangxiangdavaid/article/details/38323633
深度遍歷
 從圖的某個頂點出發(fā),訪問圖中的所有頂點,且使每個頂點僅被訪問一次。這一過程叫做圖的遍歷。
深度優(yōu)先搜索的思想:
①訪問頂點v;
②依次從v的未被訪問的鄰接點出發(fā),對圖進行深度優(yōu)先遍歷;直至圖中和v有路徑相通的頂點都被訪問;
③若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發(fā),重新進行深度優(yōu)先遍歷,直到圖中所有頂點均被訪問過為止。

比如:

image

在這里為了區(qū)分已經(jīng)訪問過的節(jié)點和沒有訪問過的節(jié)點,我們引入一個一維數(shù)組bool visited[MaxVnum]用來表示與下標對應(yīng)的頂點是否被訪問過,

流程:
? 首先輸出 V1,標記V1的flag=true;
? 獲得V1的鄰接邊 [V2 V3],取出V2,標記V2的flag=true;
? 獲得V2的鄰接邊[V1 V4 V5],過濾掉已經(jīng)flag的,取出V4,標記V4的flag=true;
? 獲得V4的鄰接邊[V2 V8],過濾掉已經(jīng)flag的,取出V8,標記V8的flag=true;
? 獲得V8的鄰接邊[V4 V5],過濾掉已經(jīng)flag的,取出V5,標記V5的flag=true;
? 此時發(fā)現(xiàn)V5的所有鄰接邊都已經(jīng)被flag了,所以需要回溯。(左邊黑色虛線,回溯到V1,回溯就是下層遞歸結(jié)束往回返)
?

image

? 回溯到V1,在前面取出的是V2,現(xiàn)在取出V3,標記V3的flag=true;
? 獲得V3的鄰接邊[V1 V6 V7],過濾掉已經(jīng)flag的,取出V6,標記V6的flag=true;
? 獲得V6的鄰接邊[V3 V7],過濾掉已經(jīng)flag的,取出V7,標記V7的flag=true;
? 此時發(fā)現(xiàn)V7的所有鄰接邊都已經(jīng)被flag了,所以需要回溯。(右邊黑色虛線,回溯到V1,回溯就是下層遞歸結(jié)束往回返)

圖例:


image.png

代碼:

 public void depthTraverse() {
        for (int i = 0; i < verticesSize; i++) {
            if (!isVisited[i]) {
                /*如果頂點沒被訪問過先打印*/
                System.out.println("打印頂點" + vertices[i]);
                traverse(i);
            }

        }
    }

    public void traverse(int i) {
        isVisited[i] = true;
        int v = getFirstBor(i);
        while (v!=-1){
            if(!isVisited[v]){
                System.out.println("visit ="+vertices[v]);
                traverse(v);
            }
            /**
             * 以下代碼會先壓入棧中,在遞歸完成后會以先進后出的
             * 形式執(zhí)行,達到從下往上把各個頂點的鄰接點打印的效果
             *           0
             *          / \
             *         1   4
             *        / \   \
             *       2   5   6
             *      /
             *     3
             */
            v = getNextBor(i,v);
        }
    }

結(jié)果:


image.png

廣度優(yōu)先遍歷
所謂廣度,就是一層一層的,向下遍歷,層層堵截,還是這幅圖,我們?nèi)绻菑V度優(yōu)先遍歷的話,我們的結(jié)果是V1 V2 V3 V4 V5 V6 V7 V8。

image

廣度優(yōu)先搜索的思想:

① 訪問頂點vi ;

② 訪問vi 的所有未被訪問的鄰接點w1 ,w2 , …wk ;

③ 依次從這些鄰接點(在步驟②中訪問的頂點)出發(fā),訪問它們的所有未被訪問的鄰接點; 依此類推,直到圖中所有訪問過的頂點的鄰接點都被訪問;

說明:

為實現(xiàn)③,需要保存在步驟②中訪問的頂點,而且訪問這些頂點的鄰接點的順序為:先保存的頂點,其鄰接點先被訪問。 這里我們就想到了用標準模板庫中的queue隊列來實現(xiàn)這種先進現(xiàn)出的服務(wù)。

老規(guī)矩我們還是走一邊流程:

說明:

?將V1加入隊列,取出V1,并標記為true(即已經(jīng)訪問),將其鄰接點加進入隊列,則 <—[V2 V3]

?取出V2,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[V3 V4 V5]

?取出V3,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[V4 V5 V6 V7]

?取出V4,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[V5 V6 V7 V8]

?取出V5,并標記為true(即已經(jīng)訪問),因為其鄰接點已經(jīng)加入隊列,則 <—[V6 V7 V8]

?取出V6,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[V7 V8]

?取出V7,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[V8]

?取出V8,并標記為true(即已經(jīng)訪問),將其未訪問過的鄰接點加進入隊列,則 <—[]

圖例


image.png
 /**
     * 廣度優(yōu)先
     */
    public void bfs(){
        for (int i = 0; i < verticesSize; i++) {
            isVisited[i]=false;
        }
        for (int i = 0; i < verticesSize; i++) {
            if(!isVisited[i]){
                isVisited[i]=true;
                System.out.println("visited vertice:"+ vertices[i]);
                bfs(i);
            }
        }
    }

    public void bfs() {
        for (int i = 0; i < verticesSize; i++) {
            if (!isVisited[i]) {
                /*如果頂點沒被訪問過先打印*/
                System.out.println("打印頂點" + vertices[i]);
                isVisited[i] = true;
                bfs(i);
            }

        }
    }

    public void bfs(int i) {
        /*自定義的隊列*/
        Queue queue = new Queue();
        int first = getFirstBor(i);
        /*如果存在且沒有被訪問過*/
        if (first != -1 && !isVisited[first]) {
            isVisited[first] = true;
            System.out.println("first " + vertices[first]);
            queue.enQueue(queue, first);

        }
        int next = getNextBor(i, first);
        while (next != -1) {
            if (!isVisited[next]) {
                isVisited[next] = true;
                System.out.println("next " + vertices[next]);
                queue.enQueue(queue, next);
            }

            next = getNextBor(i, next);
        }
        /*重復以上操作*/
        while (queue.front != queue.rear) {
            int temp = queue.array[queue.front];
            bfs(temp);
            queue.front = (queue.front + 1) % queue.MAX_LENGTH;
        }


    }

結(jié)果:


image.png

兩種算法的復雜度分析
深度優(yōu)先
數(shù)組表示:查找所有頂點的所有鄰接點所需時間為O(n2),n為頂點數(shù),算法時間復雜度為O(n2)   
廣度優(yōu)先
數(shù)組表示:查找每個頂點的鄰接點所需時間為O(n2),n為頂點數(shù),算法的時間復雜度為O(n2)
代碼

public class MyGraph {
    /*頂點集*/
    public String[] vertices;
    /*圖的邊的信息*/
    public int[][] matrix;
    /*矩陣大小*/
    public int verticesSize;
    /*被訪問過的頂點集合*/
    public boolean[] isVisited;

    public static final int MAX_WEIGHT = Integer.MAX_VALUE;

    public MyGraph(int verticesSize) {
        this.verticesSize = verticesSize;
        vertices = new String[verticesSize];
        matrix = new int[verticesSize][verticesSize];
        isVisited = new boolean[verticesSize];

        for (int i = 0; i < verticesSize; i++) {
            vertices[i] = "v" + i;
        }
    }

    /*獲取第一個鄰接點*/
    public int getFirstBor(int v) {
        for (int i = 0; i < verticesSize; i++) {
            if (matrix[v][i] > 0 && matrix[v][i] < MAX_WEIGHT) {
                // System.out.println(v + " first " + i);
                return i;
            }
        }

        return -1;
    }

    /**
     * 獲取到頂點v的鄰接點index的下一個鄰接點
     */
    public int getNextBor(int v, int index) {
        for (int i = index + 1; i < verticesSize; i++) {
//            System.out.println(v + "---" + i + "---" + matrix[v][i]);
            if (matrix[v][i] > 0 && matrix[v][i] < MAX_WEIGHT) {
                //   System.out.println(v + " next " + i);
                return i;
            }
        }
        return -1;
    }

    public void depthTraverse() {
        for (int i = 0; i < verticesSize; i++) {
            if (!isVisited[i]) {
                /*如果頂點沒被訪問過先打印*/
                System.out.println("打印頂點" + vertices[i]);
                traverse(i);
            }

        }
    }

    public void traverse(int i) {
        isVisited[i] = true;
        int v = getFirstBor(i);
        while (v != -1) {
            if (!isVisited[v]) {
                System.out.println("visit =" + vertices[v]);
                traverse(v);
            }
            /**
             * 以下代碼會先壓入棧中,在遞歸完成后會以先進后出的
             * 形式執(zhí)行,達到從下往上把各個頂點的鄰接點打印的效果
             *           0
             *          / \
             *         1   4
             *        / \   \
             *       2   5   6
             *      /
             *     3
             */
            v = getNextBor(i, v);
        }
    }

    public void dfs(int i) {
        isVisited[i] = true;
        int v = getFirstBor(i);
        if (v != -1) {
            if (!isVisited[v]) {
                System.out.println("visit =" + vertices[v]);

                traverse(v);
            }
            /**
             * 以下代碼會先壓入棧中,在遞歸完成后會以先進后出的
             * 形式執(zhí)行,達到從下往上把各個頂點的鄰接點打印的效果
             *           0
             *          / \
             *         1   4
             *        / \   \
             *       2   5   6
             *      /
             *     3
             */
            //   v = getNextBor(i,v);
        }
    }

    public void bfs() {
        for (int i = 0; i < verticesSize; i++) {
            if (!isVisited[i]) {
                /*如果頂點沒被訪問過先打印*/
                System.out.println("打印頂點" + vertices[i]);
                isVisited[i] = true;
                bfs(i);
            }

        }
    }

    public void bfs(int i) {
        /*自定義的隊列*/
        Queue queue = new Queue();
        int first = getFirstBor(i);
        /*如果存在且沒有被訪問過*/
        if (first != -1 && !isVisited[first]) {
            isVisited[first] = true;
            System.out.println("first " + vertices[first]);
            queue.enQueue(queue, first);

        }
        int next = getNextBor(i, first);
        while (next != -1) {
            if (!isVisited[next]) {
                isVisited[next] = true;
                System.out.println("next " + vertices[next]);
                queue.enQueue(queue, next);
            }

            next = getNextBor(i, next);
        }
        /*重復以上操作*/
        while (queue.front != queue.rear) {
            int temp = queue.array[queue.front];
            bfs(temp);
            queue.front = (queue.front + 1) % queue.MAX_LENGTH;
        }


    }

}

未完待續(xù)!??!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容