學習JavaScript數據結構與算法 — 圖

定義

圖和散列表二叉樹同樣,是一種非線性數據結構。如圖1所示,圖由一系列頂點以及鏈接頂點的邊構成。由一條邊鏈接在一塊兒的頂點成爲相鄰頂點,好比A和B、A和D是相鄰的,而A和E不是相鄰的。一個頂點相鄰頂點的數量叫做度,好比A的度爲三、D的度爲4。路徑指相鄰頂點的一個連續序列,如ABEI、ACDG;簡單路徑指不包含重複頂點的路徑(除環外),如ADG;環指首尾頂點相同的路徑,如ADCA,環也屬於簡單路徑。若是圖中每兩個頂點之間都有路徑相連,則稱該圖是連通的。javascript

圖1

clipboard.png

如圖2,若是圖中的邊具備方向,稱該圖爲有向圖。若是圖中的邊是雙向的,則該圖是強連通的,例如圖3中的C和D是強連通的。圖也能夠是加權的,例如圖3中的每條邊都有權值。java

圖2

clipboard.png

圖3

clipboard.png

圖能夠用來解決計算機中的不少問題,好比搜索圖中的一個特定頂點或搜索一條特定邊,尋找圖中的一條路徑(從一個頂點到另外一個頂點) ,尋找兩個頂點之間的最短路徑,以及環檢測。算法

圖的表示

圖的表示方式有多種,沒有絕對正確的表示方式,採用哪一種方式取決於圖的類型和待解決的問題。這裏介紹三種方式:鄰接矩陣、鄰接表、關聯矩陣。segmentfault

鄰接矩陣

鄰接矩陣用一個二維數組來表示圖中頂點的鏈接狀況;若是索引爲i的節點和索引爲j的節點鏈接,則array[i][j] === 1,不然array[i][j] === 0,如圖4。鄰接矩陣的缺點是,若是圖不是強連通的,矩陣中就會出現不少0,從而計算機須要浪費存儲空間來表示根本不存在的邊。例如,即便某一頂點只有一個相鄰頂點,也須要一整行來表示該頂點的鏈接狀況,數組

圖4

clipboard.png

鄰接表

鄰接表由圖中每一個頂點的相鄰頂點的列表所組成,如圖5。只要能表示一對多的數據結構,均可以用來描述鄰接表,好比多維列表(數組)、鏈表、散列表、字典等。
在大多數狀況下,鄰接表是更好的選擇,但鄰接矩陣也有其優勢,好比要判斷頂點A和B是否相鄰,鄰接矩陣比鄰接表要快。數據結構

圖5測試

clipboard.png

關聯矩陣

在關聯矩陣表示的圖中,矩陣的行表示頂點,列表示邊。如圖6所示,咱們使用二維數組來表示二者之間的連通性,若是頂點A是邊E的入射點,則array[A][E] === 1;不然,array[A][E] === 0。
關聯矩陣一般用於邊的數量比頂點少的狀況下,以節省空間和內存。如圖6,頂點數是5,邊的數量是6,用鄰接矩陣表示圖須要的空間是5*5=25,而使用關聯矩陣表示圖須要的空間是5*6=30。this

圖6spa

clipboard.png

建立圖類

首先首先聲明類的骨架:3d

function Graph() {
    var vertices = [];
    var adjList = new Dictionary();
}

其中Dictionary類的實現參考以前的文章字典
咱們使用數組 vertices 來存儲圖中全部頂點的名字,以及字典 adjList 來存儲鄰接表。字典將會使用頂點的名字做爲鍵,鄰接頂點列表做爲值。
接下來實現向圖中添加頂點的方法:

this.addVertex = function(v){
    vertices.push(v);
    adjList.set(v, []);
};

該方法接受頂點 v 做爲參數。咱們將該頂點添加到頂點列表 vertices 中,而且在鄰接表中,設置頂點 v 做爲鍵對應的字典值爲一個空數組。
接着實現一個點到另外一個點的鏈接:

this.addEdge = function(v, w){
    adjList.get(v).push(w);
    adjList.get(w).push(v);
};

這個方法接受兩個頂點做爲參數。首先,咱們經過將 w 加入到 v 的鄰接表中,添加了一條自頂
點 v 到頂點 w 的邊。若是是有向圖,則只須要該方法的第一行代碼就好了。咱們這裏要實現無向圖,咱們須要添加一條自 w 向 v 的邊,即該方法的第二行代碼。
使用該圖類進行簡單的測試:

var graph = new Graph();
var myVertices = ['A','B','C','D'];
for (var i=0; i<myVertices.length; i++){
    graph.addVertex(myVertices[i]); // 添加圖的頂點
}
// 添加圖的邊
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'D');

上述代碼生成圖對應的鄰接表爲:
A -> B C
B -> A D
C -> A D
D -> B C

圖的遍歷

有兩種算法能夠對圖進行遍歷:廣度優先搜索(Breadth-First Search,BFS)和深度優先搜索(Depth-First Search,DFS)。圖遍歷能夠用來尋找特定的頂點或尋找兩個頂點之間的路徑,檢查圖是否連通,檢查圖是否含有環等。
圖遍歷算法的思路是追蹤每一個第一次訪問的節點,而且追蹤有哪些節點尚未被徹底探索。對於兩種圖遍歷算法,都須要明確指出第一個被訪問的頂點。
徹底探索一個頂點須要查看該頂點的每一條邊。對於每一條邊所鏈接的沒有被訪問過的頂點,將其標註爲被發現的,並將其加進待訪問頂點列表中。
咱們用三種狀態來反映頂點的狀態:

  • 白色:表示該頂點尚未被訪問。
  • 灰色:表示該頂點被訪問過,但並未被探索過。
  • 黑色:表示該頂點被訪問過且被徹底探索過。

由於只有這三種狀態,初始狀態是白色,所以每一個頂點至多訪問兩次,這樣作可以保證算法的效率。
廣度優先搜索算法和深度優先搜索算法基本上是相同的,只是待訪問頂點列表的數據結構不一樣。
廣度優先搜索算法:數據結構是隊列。經過將頂點存入隊列中,最早入隊列的頂點先被探索。
深度優先搜索算法:數據結構是。經過將頂點存入棧中,沿着路徑探索頂點,存在新的相鄰頂點就去訪問。

相關文章
相關標籤/搜索