圖就是由若干個頂點和邊鏈接起來的一種結構。不少東西均可以用圖來講明,例如人際關係,或者地圖。數據結構
其中圖還分爲有向圖和無向圖。
以下就是有向圖函數
對於圖這種關係,能夠經過兩種方式來存儲。this
將每一個頂點與其相鄰的頂點存儲起來。code
將頂點間的相鄰關係用0和1來表示,0表示不相鄰,1表示相鄰。隊列
以下采用鄰接表結構實現。get
class Graph { constructor() { this.vertices = []; this.adjList = new Map(); } }
verrices存儲全部的頂點,adjList存儲頂點間的關係。it
addVertex(v) { this.vertices.push(v);{1} this.adjList.set(v, []);{2} } addEdge(v, w) { this.adjList.get(v).push(w); this.adjList.get(w).push(v); }
addVertex函數爲新增頂點。{2}是爲新增的頂點初始化一個爲空的鄰接點。
addEdge函數爲關聯兩個頂點爲鄰接點。因爲本次示例的是無向圖,所以每一個頂點都互相增長爲鄰接點。class
圖的遍歷分爲廣度優先遍歷和深度優先遍歷。
廣度優先遍歷就是從一個頂點開始,一層一層的遍歷頂點。而深度優先遍歷,是從一個頂點開始,選擇一個路徑一直深刻遍歷,直到到達該路徑的盡頭,再返回去,按照一樣的方式,遍歷其餘頂點。
不管廣度優先仍是深度優先,大體原理都是,建立一個字段,從一個頂點開始,將其鄰接點一個個存入該字段中,惟一的區別是,廣度優先遍歷,該字段用隊列存儲,而深度優先遍歷用棧存儲。
然而因爲本次示例的圖爲無向圖,會出現重複遍歷的狀況,例如A頂點的鄰接點有B,B的鄰接點有A。所以須要建立一個類,來維護這些頂點哪些已經被遍歷過了,哪些尚未遍歷。原理
class GraphStatus { constructor() { this.status = {}; } detect(key) { this.status[key] = true; } isDetected(key) { return !!this.status[key]; } }
以下是廣度優先遍歷List
bfs(v, cb) { const queue = [], graphStatus = new GraphStatus(); queue.push(v); while (queue.length > 0) { const u = queue.shift(); graphStatus.detect(u); this.adjList.get(u).forEach(item => { if (!graphStatus.isDetected(item)) { graphStatus.detect(item); queue.push(item); } }); if (cb) { cb(u); } } }
以下是深度優先遍歷
dfs(v, cb) { const stack = [], colorStatus = new GraphStatus(); stack.push(v); while (stack.length > 0) { const u = stack.pop(); colorStatus.detect(u); this.adjList.get(u).forEach(item => { if (!colorStatus.isDetected(item)) { colorStatus.detect(item); stack.push(item); } }); cb(u); } }
基於廣度優先遍歷,能夠很輕易的算出最短路徑。
findDepth(v) { let queue = [], colorStatus = new GraphStatus(), vPath = { [v]: [v] }, vDepth = {}, depth = 0; queue.push(v); while (queue.length > 0) { depth++; const u = queue.shift(); colorStatus.detect(u); // 相鄰頂點 const edgeVertex = this.adjList.get(u); edgeVertex.forEach(item => { if (!colorStatus.isDetected(item)) { // 深度統計 vDepth[item] = depth; // 路徑統計 vPath[item] = [...vPath[u], item]; colorStatus.detect(item); queue.push(item); } }); } return { depth: vDepth, path: vPath }; }