數據結構與算法之圖

定義:圖由邊的集合及頂點的集合組成。頂點也有權重, 也稱爲成本。javascript

若是一個圖的頂點對是有序的, 則能夠稱之爲有向圖。在對有向圖中的頂點對排序後, 即可以在兩
個頂點之間繪製一個箭頭。 有向圖代表了頂點的流向。java

1556459287978

若是圖是無序的, 則稱之爲無序圖, 或無向圖。算法

1556459315157

圖中的一系列頂點構成路徑, 路徑中全部的頂點都由邊鏈接。 路徑的長度用路徑中第一個頂點到最後一個頂點之間邊的數量表示。 由指向自身的頂點組成的路徑稱爲環, 環的長度爲 0。數組

圈是至少有一條邊的路徑, 且路徑的第一個頂點和最後一個頂點相同。 不管是有向圖仍是無向圖, 只要是沒有重複邊或重複頂點的圈, 就是一個簡單圈。 除了第一個和最後一個頂點之外, 路徑的其餘頂點有重複的圈稱爲平凡圈。數據結構

若是兩個頂點之間有路徑, 那麼這兩個頂點就是強連通的, 反之亦然。 若是有向圖的全部的頂點都是強連通的, 那麼這個有向圖也是強連通的。函數

表示頂點

用Vertex類表示節點,Vertex 類有兩個數據成員: 一個用於標識頂點, 另外一個是代表這個頂點是否被訪問過的布爾值。它們分別被命名爲 label 和 wasVisited。this

function Vertex(label){
    this.label = label;
}

表示邊

用鄰接表或鄰接表數組來表示邊。數組的索引表示頂點,元素是一個數組,裏面的成員是與該頂點相連的其餘頂點。所以鄰接表是一個二維的數組。prototype

1556547359298

構建圖與搜索圖

搜索圖分兩種方式:深度優先和廣度優先。code

廣度優先搜索算法:數據結構是隊列。經過將頂點存入隊列中,最早入隊列的頂點先被探索。
深度優先搜索算法:數據結構是棧。經過將頂點存入棧中,沿着路徑探索頂點,存在新的相鄰頂點就去訪問。排序

深度優先

深度優先搜索包括從一條路徑的起始頂點開始追溯, 直到到達最後一個頂點, 而後回溯,繼續追溯下一條路徑, 直到到達最後的頂點, 如此往復, 直到沒有路徑爲止。

1556547700281

思路:訪問一個沒有訪問過的頂點, 將它標記爲已訪問, 再遞歸地去訪問在初始頂點的鄰接表中其餘沒有訪問過的頂點。

廣度優先

廣度優先搜索從第一個頂點開始, 嘗試訪問儘量靠近它的頂點。 本質上, 這種搜索在圖上是逐層移動的, 首先檢查最靠近第一個頂點的層, 再逐漸向下移動到離起始頂點最遠的層。

1557063097683

用JS實現以上兩種搜索方法:

// 建立圖類
function Graph(v){
    this.vertices = v;  // 共有多少個節點
    this.edges = 0;  // 有多少條邊
    this.adj = [];  // 鄰接表數組
    for(var i = 0;i<this.vertices;i++){
        this.adj[i] = [];
    }
    this.marked = [];  // 用於搜索
    for(var i = 0;i<this.vertices;i++){
        this.marked[i] = false;
    }
    this.edgeTo = [];  // 用於廣度優先搜索查找最短路徑

}
Graph.prototype = {
    constructor: Graph,

    // 在兩個頂點間畫一條邊
    addEdge(v, w){
        this.adj[v].push(w);
        this.adj[w].push(v);
        this.edges++;
    },
    showGraph(){
        for(var i = 0;i<this.vertices;i++){
            var str = '';
            for(var j = 0;j<this.vertices;j++){
                if(this.adj[i][j] !== undefined){
                    str += this.adj[i][j] + ' ';
                }
            }
            console.log(i + "-> " + str + '\n');
        }
    },

    // 深度優先搜索函數depth first searching,遞歸實現
    dfs(v){
        this.marked[v] = true;
        if(this.adj[v].length){
            console.log("Visited vertex: " + v);
            for(var w of this.adj[v]){
                if(!this.marked[w]){
                    this.dfs(w);
                }
            }
        }
    },

    // 廣度優先搜索函數,無遞歸實現
    bfs(s){
        var queue = [];
        this.marked[s] = true;
        queue.push(s);
        while(queue.length > 0){
            var v = queue.shift();
            console.log("Visited vertex: " + v);
            for(var w of this.adj[v]){
                if(!this.marked[w]){
                    this.edgeTo[w] = v; 
                    this.marked[w] = true;
                    queue.push(w);
                }
            }                            
        }
    }
}

利用廣度優先搜索方法查找最短路徑(查找頂點1和頂點4之間的最短路徑):

123

須要再擴展一個方法,經過執行一次廣度優先搜索後獲得的edgeTo數組來找到1和4節點之間相互鏈接的節點:

Graph.prototype.pathTo = function(w, v){
    this.bfs(w);
    var source = w;  // 頂點w做爲起點
    if(!this.hasPathTo(v)){
        return undefined;
    }
    var path = [];  // 保存路徑,不過是從終點到起點的
    for(var i = v; i != source; i = this.edgeTo[i]){
        path.push(i);
    }
    path.push(source);
    return path;
}
Graph.prototype.hasPathTo(v){
    return this.marked[v];
}

var g = new Graph(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 5);
g.addEdge(2, 3);
g.addEdge(3, 5);
g.addEdge(2, 4);
g.showGraph();
g.pathTo(4, 1);  // [1, 0, 2, 4]
相關文章
相關標籤/搜索