圖
github直達地址 https://github.com/fanshyiis/...node
在計算機科學中,一個圖就是一些頂點的集合,這些頂點經過一系列邊結對(鏈接)。頂點用圓圈表示,邊就是這些圓圈之間的連線。頂點之間經過邊鏈接。git
注意:頂點有時也稱爲節點或者交點,邊有時也稱爲連接。
一個圖能夠表示一個社交網絡,每個人就是一個頂點,互相認識的人之間經過邊聯繫。es6
理論上,圖就是一堆頂點和邊對象而已,可是怎麼在代碼中來描述呢?
有兩種主要的方法:鄰接列表和鄰接矩陣。github
鄰接列表:在鄰接列表實現中,每個頂點會存儲一個從它這裏開始的邊的列表。好比,若是頂點A 有一條邊到B、C和D,那麼A的列表中會有3條邊算法
鄰接列表只描述了指向外部的邊。A 有一條邊到B,可是B沒有邊到A,因此 A沒有出如今B的鄰接列表中。查找兩個頂點之間的邊或者權重會比較費時,由於遍歷鄰接列表直到找到爲止。數組
鄰接矩陣:在鄰接矩陣實現中,由行和列都表示頂點,由兩個頂點所決定的矩陣對應元素表示這裏兩個頂點是否相連、若是相連這個值表示的是相連邊的權重。例如,若是從頂點A到頂點B有一條權重爲 5.6 的邊,那麼矩陣中第A行第B列的位置的元素值應該是5.6:網絡
往這個圖中添加頂點的成本很是昂貴,由於新的矩陣結果必須從新按照新的行/列建立,而後將已有的數據複製到新的矩陣中。
因此使用哪個呢?大多數時候,選擇鄰接列表是正確的。下面是兩種實現方法更詳細的比較。
假設 V 表示圖中頂點的個數,E 表示邊的個數。數據結構
「檢查相鄰性」 是指對於給定的頂點,嘗試肯定它是不是另外一個頂點的鄰居。在鄰接列表中檢查相鄰性的時間複雜度是O(V),由於最壞的狀況是一個頂點與每個頂點都相連。
在 稀疏圖的狀況下,每個頂點都只會和少數幾個頂點相連,這種狀況下相鄰列表是最佳選擇。若是這個圖比較密集,每個頂點都和大多數其餘頂點相連,那麼相鄰矩陣更合適。koa
瞭解了圖的基本定義後咱們來看下如何用es6的類class思想來實現圖類yii
首先咱們先定義兩個輔助類
class Dictionary { constructor () { this.items = {} } has (key) { return key in this.items } set (key, val) { this.items[key] = val } delete (key) { if (this.has(key)) { delete this.items[key] return true } else { return false } } get (key) { return this.has(key)? this.items[key] : undefined } values () { let values = [] for (let k in this.items) { if (this.has(k)) { values.push(this.items[k]) } } return values } } class Queue { constructor () { this.items = [] } enqueue (element) { this.items.push(element) } dequeue () { return this.items.shift() } isEmpty() { return this.items.length === 0 } }
Dictionary 字典類來輔助存貯鍵值對
Queue 隊列類來存貯隊列
//定義class Graph class Graph { constructor () { this.vertices = [] this.adjList = new Dictionary() } }
定義Graph類而且在構造函數裏初始化字段
vertices 存儲點信息
adjList 存儲頂點間的連接關係
addVertex (v) { this.vertices.push(v) this.adjList.set(v, []) } addEdge (v, w) { this.adjList.get(v).push(w) }
addVertex 添加頂點的方法,存貯在數組vertices,而且初始化adjList字典裏的值
addEdge 添加單向邊 接收兩個值 在鄰接字典里加上從第一個頂點到第二個的關係
到這 一個基本的類就完成了,咱們能夠經過測試代碼來測試
et graph = new Graph() let mv = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] mv.map(val => { graph.addVertex(val) }) graph.addEdge('A', 'B') graph.addEdge('A', 'C') graph.addEdge('A', 'D') graph.addEdge('C', 'D') graph.addEdge('C', 'G') graph.addEdge('D', 'G') graph.addEdge('D', 'H') graph.addEdge('B', 'E') graph.addEdge('B', 'F') graph.addEdge('E', 'I')
獲得的圖
下面咱們來定義一個功能來打印圖
toString () { let s = '' for (let i = 0; i < this.vertices.length; i++) { s += this.vertices[i] + '->' let neighbors = this.adjList.get(this.vertices[i]) for (let j = 0; j < neighbors.length; j++) { s += neighbors[j] + ' ' } s += '\n' } return s }
執行文件 node graph.js
獲得結果
A->B C D B->E F C->D G D->G H E->I F-> G->
好到這就基本完成類的結構了,下面進行圖的遍歷
廣度優先 - 數據結構 隊列
先上代碼
BFS (v, callback) { let color = this.initializeColor(), queue = new Queue() queue.enqueue(v) while (!queue.isEmpty()) { let u = queue.dequeue(), neighbors = this.adjList.get(u) color[u] = 'grey' neighbors.map(val => { if (color[val] === 'white') { color[val] = 'grey' queue.enqueue(val) } }) color[u] = 'black' if (callback) { callback(u) } } }
基本思想以下
1.初始化各個頂點的顏色(白色 - 未訪問; 灰色 - 已發現; 黑色 - 已經探索完)
2.初始化一個隊列
3.首先隊列入頂點 v
4.若是隊列不會空,則取隊列第一個進行探索
5.探索過程當中獲取當前頂點的全部鄰接頂點,並推動隊列
6.完成5後,標記當前爲黑色
圖示例
A 探索 隊列入 B - C - D
完成 A探索
出B 探索B 隊列再入 E - F 當前 CDEF
完成 B探索
出C 探索 隊列再入 G 當前DEFG
。。。
直到全部頂點探索完
深度優先 - 數據結構 棧
先上代碼
DFS (callback) { let color = this.initializeColor() this.vertices.map(val => { if (color[val] === 'white') { this.dfsVisit(val, color, callback) } }) } dfsVisit (u, color, callback) { color[u] = 'grey' if (callback) { callback(u) } let neighbors = this.adjList.get(u) neighbors.map(val => { if (color[val] === 'white') { this.dfsVisit(val, color, callback) } }) color[u] = 'black' }
深度優先的原則以棧爲數據結構
基本思想以下
1.初始化各個頂點的顏色(白色 - 未訪問; 灰色 - 已發現; 黑色 - 已經探索完)
2.對頂點進行遍歷並進行dfsVisit函數探索
3.優先進行最新探索的邊進行深度探索,完成後標記探索完成
基本步驟以下
探索A
發現BCD
探索B
發現EF
探索E
發現I
探索I,完畢 標記I爲以探索
回到上個分支遍歷接着執行探索F
完成
標記F爲以探索
。。。
直到返回到頂點A
完成探索
具體還有PLus版的深度和廣度優先的算法,具體代碼奉上
/* * @Author: koala_cpx * @Date: 2019-01-24 10:48:01 * @Last Modified by: koala_cpx * @Last Modified time: 2019-01-24 10:56:33 */ class Dictionary { constructor () { this.items = {} } has (key) { return key in this.items } set (key, val) { this.items[key] = val } delete (key) { if (this.has(key)) { delete this.items[key] return true } else { return false } } get (key) { return this.has(key)? this.items[key] : undefined } values () { let values = [] for (let k in this.items) { if (this.has(k)) { values.push(this.items[k]) } } return values } } class Queue { constructor () { this.items = [] } enqueue (element) { this.items.push(element) } dequeue () { return this.items.shift() } isEmpty() { return this.items.length === 0 } } class Graph { constructor () { this.vertices = [] this.adjList = new Dictionary() this.time = 0 } addVertex (v) { this.vertices.push(v) this.adjList.set(v, []) } addEdge (v, w) { this.adjList.get(v).push(w) // this.adjList.get(w).push(v) } BFS (v, callback) { let color = this.initializeColor(), queue = new Queue() queue.enqueue(v) while (!queue.isEmpty()) { let u = queue.dequeue(), neighbors = this.adjList.get(u) color[u] = 'grey' neighbors.map(val => { if (color[val] === 'white') { color[val] = 'grey' queue.enqueue(val) } }) color[u] = 'black' if (callback) { callback(u) } } } BFSPlus (v) { let color = this.initializeColor(), queue = new Queue(), d = [], pred = [] queue.enqueue(v) this.vertices.map(val => { d[val] = 0 pred[val] = null }) while (!queue.isEmpty()) { let u = queue.dequeue(), neighbors = this.adjList.get(u) color[u] = 'grey' neighbors.map(val => { if (color[val] === 'white') { color[val] = 'grey' d[val] = d[u] + 1 pred[val] = u queue.enqueue(val) } }) color[u] = 'black' } return { distances: d, predecessors: pred } } DFS (callback) { let color = this.initializeColor() this.vertices.map(val => { if (color[val] === 'white') { this.dfsVisit(val, color, callback) } }) } DFSPlus () { let color = this.initializeColor(), d = [], f = [], p = [] this.time = 0 this.vertices.map(val => { f[val] = 0 d[val] = 0 p[val] = null }) this.vertices.map(val => { if (color[val] === 'white') { this.dfsPlusVisit(val, color, d, f, p) } }) return { discovery: d, finished: f, predecessors: p } } dfsPlusVisit (u, color, d, f, p) { console.log('discovery' + u) color[u] = 'grey' d[u] = ++this.time let neighbors = this.adjList.get(u) neighbors.map(val => { if (color[val] === 'white') { p[val] = u this.dfsPlusVisit(val, color, d, f, p) } }) color[u] = 'black' f[u] = ++this.time console.log('explored' + u) } dfsVisit (u, color, callback) { color[u] = 'grey' if (callback) { callback(u) } let neighbors = this.adjList.get(u) neighbors.map(val => { if (color[val] === 'white') { this.dfsVisit(val, color, callback) } }) color[u] = 'black' } outPut(obj) { let fromVertex = this.vertices[0], { predecessors } = obj this.vertices.map(val => { let path = new Array() for (var v = val; v !== fromVertex; v = predecessors[v]) { path.push(v) } path.push(fromVertex) let s = path.pop() while (path.length !== 0) { s += ' -- ' + path.pop() } console.log(s) }) } initializeColor () { let color = [] this.vertices.map(val => { color[val] = 'white' }) return color } toString () { let s = '' for (let i = 0; i < this.vertices.length; i++) { s += this.vertices[i] + '->' let neighbors = this.adjList.get(this.vertices[i]) for (let j = 0; j < neighbors.length; j++) { s += neighbors[j] + ' ' } s += '\n' } return s } } let a = new Dictionary() a.set('ss', 1111) a.set('s1', 1111) a.set('s2', 1112) a.set('s3', 1114) a.delete('s2') console.log(a.has('s3')) console.log(a.values()) let graph = new Graph() let mv = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] mv.map(val => { graph.addVertex(val) }) graph.addEdge('A', 'B') graph.addEdge('A', 'C') graph.addEdge('A', 'D') graph.addEdge('C', 'D') graph.addEdge('C', 'G') graph.addEdge('D', 'G') graph.addEdge('D', 'H') graph.addEdge('B', 'E') graph.addEdge('B', 'F') graph.addEdge('E', 'I') console.log(graph.toString()) function print(val) { console.log('vis' + val) } graph.BFS('A', print) console.log(graph.BFSPlus("A")) graph.outPut(graph.BFSPlus("A")) graph.DFS(print) console.log(graph.DFSPlus()) let graph2 = new Graph() let mv2 = ['A', 'B', 'C', 'D', 'E', 'F'] mv2.map(val => { graph2.addVertex(val) }) graph2.addEdge('A', 'C') graph2.addEdge('A', 'D') graph2.addEdge('B', 'D') graph2.addEdge('B', 'E') graph2.addEdge('C', 'F') graph2.addEdge('F', 'E') let r = graph2.DFSPlus() console.log(r)
github直達地址 https://github.com/fanshyiis/...