ES6的JavaScript数据结构实现之图
内容:图,图的遍历(广度优先搜索BFS,深度优先搜索DFS),最短路径算法,最小生成树算法。(未完成,待继续)
所有源码在我的Github上(如果觉得不错记得给星鼓励我哦):ES6的JavaScript数据结构实现之图
一、基础数据结构
1、图(创建Graph类)
1 // import dictionary 2 class ValuePair { 3 constructor(key, value) { 4 this.key = key; 5 this.value = value; 6 } 7 8 toString() { 9 return `[#${this.key}: ${this.value}]`; 10 } 11 } 12 13 function defaultToString(item) { 14 if (item === null) { 15 return 'NULL'; 16 } if (item === undefined) { 17 return 'UNDEFINED'; 18 } if (typeof item === 'string' || item instanceof String) { 19 return `${item}`; 20 } 21 return item.toString(); 22 } 23 24 class Dictionary { 25 constructor(toStrFn = defaultToString) { 26 this.toStrFn = toStrFn; 27 this.table = {}; 28 } 29 hasKey(key) { 30 return this.table[this.toStrFn(key)] != null; 31 } 32 set(key, value) { 33 if (key != null && value != null) { 34 const tableKey = this.toStrFn(key); 35 this.table[tableKey] = new ValuePair(key,value); 36 return true; 37 } 38 return false; 39 } 40 remove(key) { 41 if (this.hasKey(key)) { 42 delete this.table[this.toStrFn(key)]; 43 return true; 44 } 45 return false; 46 } 47 get(key) { 48 const valuePair = this.table[this.toStrFn(key)]; 49 return valuePair == null ? undefined : valuePair.value; 50 } 51 keyValues() { 52 return Object.values(this.table); 53 } 54 values() { 55 return this.keyValues().map(valuePair => valuePair.value); 56 } 57 58 keys() { 59 return this.keyValues().map(valuePair => valuePair.key); 60 } 61 forEach(callbackFn) { 62 const valuePairs = this.keyValues(); 63 for (let i = 0; i < valuePairs.length; i++) { 64 const result = callbackFn(valuePairs[i].key, valuePairs[i].value); 65 if (result === false) { 66 break; 67 } 68 } 69 } 70 71 isEmpty() { 72 return this.size() === 0; 73 } 74 75 size() { 76 return Object.keys(this.table).length; 77 } 78 79 clear() { 80 this.table = {}; 81 } 82 83 toString() { 84 if (this.isEmpty()) { 85 return ''; 86 } 87 const valuePairs = this.keyValues(); 88 let objString = `${valuePairs[0].toString()}`; 89 for (let i = 1; i < valuePairs.length; i++) { 90 objString = `${objString},${valuePairs[i].toString()}`; 91 } 92 return objString; 93 } 94 95 96 } 97 98 class Graph { 99 constructor (isDirected = false) { 100 this.isDirected = isDirected; 101 this.vertices = []; 102 this.adjList = new Dictionary(); 103 } 104 addVertex(v) { 105 if (!this.vertices.includes(v)) { 106 this.vertices.push(v); 107 this.adjList.set(v, []); 108 } 109 } 110 addEdge(a, b ) { 111 if (!this.adjList.get(a)) { 112 this.addVertex(a); 113 } 114 if (!this.adjList.get(b)) { 115 this.adjList(b); 116 } 117 this.adjList.get(a).push(b); 118 if (this.isDirected !== true) { 119 this.adjList.get(b).push(a); 120 } 121 } 122 getVertices() { 123 return this.vertices; 124 } 125 getAdjList() { 126 return this.adjList; 127 } 128 toString() { 129 let s = ''; 130 for (let i = 0; i < this.vertices.length; i++) { 131 s += `${this.vertices[i]} -> `; 132 const neighbors = this.adjList.get(this.vertices[i]); 133 for (let j = 0; j < neighbors.length; j++) { 134 s +=`${neighbors[j]} `; 135 } 136 s += '\n'; 137 } 138 return s; 139 } 140 141 } 142 143 144 const graph = new Graph(); 145 const myVertices = ['A','B','C','D','E','F','G','H','I']; 146 for (let i = 0; i < myVertices.length; i++) { 147 graph.addVertex(myVertices[i]); 148 } 149 graph.addEdge('A','B'); 150 graph.addEdge('A','C'); 151 graph.addEdge('A', 'D'); 152 graph.addEdge('C', 'D'); 153 graph.addEdge('C', 'G'); 154 graph.addEdge('D', 'G'); 155 graph.addEdge('D', 'H'); 156 graph.addEdge('B', 'E'); 157 graph.addEdge('B', 'F'); 158 graph.addEdge('E', 'I'); 159 console.log(graph.toString());
二、简单应用
1、图的遍历
1.1 广度优先搜索(算法工作原理实现;使用BFS寻找最短路径)
概念:广度优先搜索算法会从指定的第一个顶点开始遍历图,先访问其所有的邻点(相邻顶点),就像一次访问图的一层(先宽后深地访问顶点)。使用BFS寻找最短路径(给定一个图G和源顶点v,找出每个顶点u和v之间最短路径的距离(以边的数量计))。
注:在上面的最短路径算法中,图不是加权的。如果要计算加权图中的最短路径,广度优先搜索未必合适,要考虑使用Dijkstra算法、Floyd-Warshell算法、Bellman-Ford算法、A*搜索算法(前两者的实现在本博客的最后)。
1 // import dictionary 2 class ValuePair { 3 constructor(key, value) { 4 this.key = key; 5 this.value = value; 6 } 7 8 toString() { 9 return `[#${this.key}: ${this.value}]`; 10 } 11 } 12 13 function defaultToString(item) { 14 if (item === null) { 15 return 'NULL'; 16 } if (item === undefined) { 17 return 'UNDEFINED'; 18 } if (typeof item === 'string' || item instanceof String) { 19 return `${item}`; 20 } 21 return item.toString(); 22 } 23 24 class Dictionary { 25 constructor(toStrFn = defaultToString) { 26 this.toStrFn = toStrFn; 27 this.table = {}; 28 } 29 hasKey(key) { 30 return this.table[this.toStrFn(key)] != null; 31 } 32 set(key, value) { 33 if (key != null && value != null) { 34 const tableKey = this.toStrFn(key); 35 this.table[tableKey] = new ValuePair(key,value); 36 return true; 37 } 38 return false; 39 } 40 remove(key) { 41 if (this.hasKey(key)) { 42 delete this.table[this.toStrFn(key)]; 43 return true; 44 } 45 return false; 46 } 47 get(key) { 48 const valuePair = this.table[this.toStrFn(key)]; 49 return valuePair == null ? undefined : valuePair.value; 50 } 51 keyValues() { 52 return Object.values(this.table); 53 } 54 values() { 55 return this.keyValues().map(valuePair => valuePair.value); 56 } 57 58 keys() { 59 return this.keyValues().map(valuePair => valuePair.key); 60 } 61 forEach(callbackFn) { 62 const valuePairs = this.keyValues(); 63 for (let i = 0; i < valuePairs.length; i++) { 64 const result = callbackFn(valuePairs[i].key, valuePairs[i].value); 65 if (result === false) { 66 break; 67 } 68 } 69 } 70 71 isEmpty() { 72 return this.size() === 0; 73 } 74 75 size() { 76 return Object.keys(this.table).length; 77 } 78 79 clear() { 80 this.table = {}; 81 } 82 83 toString() { 84 if (this.isEmpty()) { 85 return ''; 86 } 87 const valuePairs = this.keyValues(); 88 let objString = `${valuePairs[0].toString()}`; 89 for (let i = 1; i < valuePairs.length; i++) { 90 objString = `${objString},${valuePairs[i].toString()}`; 91 } 92 return objString; 93 } 94 95 96 } 97 98 class Graph { 99 constructor (isDirected = false) { 100 this.isDirected = isDirected; 101 this.vertices = []; 102 this.adjList = new Dictionary(); 103 } 104 addVertex(v) { 105 if (!this.vertices.includes(v)) { 106 this.vertices.push(v); 107 this.adjList.set(v, []); 108 } 109 } 110 addEdge(a, b ) { 111 if (!this.adjList.get(a)) { 112 this.addVertex(a); 113 } 114 if (!this.adjList.get(b)) { 115 this.adjList(b); 116 } 117 this.adjList.get(a).push(b); 118 if (this.isDirected !== true) { 119 this.adjList.get(b).push(a); 120 } 121 } 122 getVertices() { 123 return this.vertices; 124 } 125 getAdjList() { 126 return this.adjList; 127 } 128 toString() { 129 let s = ''; 130 for (let i = 0; i < this.vertices.length; i++) { 131 s += `${this.vertices[i]} -> `; 132 const neighbors = this.adjList.get(this.vertices[i]); 133 for (let j = 0; j < neighbors.length; j++) { 134 s +=`${neighbors[j]} `; 135 } 136 s += '\n'; 137 } 138 return s; 139 } 140 141 } 142 //import Queue 143 class Queue{ 144 constructor(){ 145 this.count = 0; 146 this.lowestCount = 0; 147 this.items ={}; 148 } 149 150 151 enqueue(element) { 152 this.items[this.count] = element; 153 this.count++; 154 } 155 156 dequeue() { 157 if (this.isEmpty()) { 158 return undefined; 159 } 160 const result = this.items[this.lowestCount]; 161 delete this.items[this.lowestCount]; 162 this.lowestCount++; 163 return result; 164 } 165 166 peek() { 167 if (this.isEmpty()){ 168 return undefined; 169 } 170 return this.items[this.lowestCount]; 171 } 172 173 isEmpty() { 174 return this.size() === 0; 175 } 176 size() { 177 return this.count - this.lowestCount; 178 } 179 clear(){ 180 this.items = {}; 181 this.count = 0; 182 this.lowestCount = 0; 183 } 184 toString(){ 185 if (this.isEmpty()){ 186 return ''; 187 } 188 let objString = `${this.items[this.lowestCount]}` 189 for (let i= this.lowestCount + 1; i < this.count;i++){ 190 objString = `${objString},${this.items[i]}`; 191 } 192 return objString; 193 } 194 } 195 196 // stack for shortest path - BFS 197 class Stack { 198 constructor() { 199 this.count = 0; 200 this.items = {}; 201 } 202 203 push(element) { 204 this.items[this.count] = element; 205 this.count++; 206 } 207 208 pop() { 209 if (this.isEmpty()) { 210 return undefined; 211 } 212 this.count--; 213 const result = this.items[this.count]; 214 delete this.items[this.count]; 215 return result; 216 } 217 218 peek() { 219 if (this.isEmpty()) { 220 return undefined; 221 } 222 return this.items[this.count - 1]; 223 } 224 225 isEmpty() { 226 return this.count === 0; 227 } 228 229 size() { 230 return this.count; 231 } 232 233 clear() { 234 /* while (!this.isEmpty()) { 235 this.pop(); 236 } */ 237 this.items = {}; 238 this.count = 0; 239 } 240 241 toString() { 242 if (this.isEmpty()) { 243 return ''; 244 } 245 let objString = `${this.items[0]}`; 246 for (let i = 1; i < this.count; i++) { 247 objString = `${objString},${this.items[i]}`; 248 } 249 return objString; 250 } 251 } 252 253 254 const Colors = { 255 WHITE: 0, 256 GREY: 1, 257 BLACK: 2 258 }; 259 260 261 const initializeColor = vertices => { 262 const color = {}; 263 for (let i = 0; i < vertices.length; i++) { 264 color[vertices[i]] = Colors.WHITE; 265 } 266 return color; 267 }; 268 269 const breadthFirstSearch = (graph, startVertex, callback) => { 270 const vertices = graph.getVertices(); 271 const adjList = graph.getAdjList(); 272 const color = initializeColor(vertices); 273 const queue = new Queue; 274 275 queue.enqueue(startVertex); 276 while (!queue.isEmpty()) { 277 const u = queue.dequeue(); 278 const neighbors = adjList.get(u); 279 color[u] = Colors.GREY; 280 for (let i = 0; i < neighbors.length; i++) { 281 const w = neighbors[i]; 282 if (color[w] === Colors.WHITE) { 283 color[w] = Colors.GREY; 284 queue.enqueue(w); 285 } 286 } 287 color[u] = Colors.BLACK; 288 if (callback) { 289 callback(u); 290 } 291 292 } 293 294 }; 295 296 const BFS = (graph, startVertex) => { 297 const vertices = graph.getVertices(); 298 const adjList = graph.getAdjList(); 299 const color = initializeColor(vertices); 300 const queue = new Queue; 301 const distances = {}; 302 const predecessors = {}; 303 queue.enqueue(startVertex); 304 305 for (let i = 0; i < vertices.length; i++) { 306 distances[vertices[i]] = 0; 307 predecessors[vertices[i]] = null; 308 } 309 while (!queue.isEmpty()) { 310 const u = queue.dequeue(); 311 const neighbors = adjList.get(u); 312 color[u] = Colors.GREY; 313 for (let i = 0; i < neighbors.length; i++) { 314 const w = neighbors[i]; 315 if (color[w] === Colors.WHITE) { 316 color[w] = Colors.GREY; 317 distances[w] = distances[u] + 1; 318 predecessors[w] = u; 319 queue.enqueue(w); 320 } 321 } 322 color[u] = Colors.BLACK; 323 } 324 return { 325 distances, 326 predecessors 327 }; 328 }; 329 330 331 const graph = new Graph(); 332 333 const myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; 334 335 for (let i = 0; i < myVertices.length; i++) { 336 graph.addVertex(myVertices[i]); 337 } 338 graph.addEdge('A', 'B'); 339 graph.addEdge('A', 'C'); 340 graph.addEdge('A', 'D'); 341 graph.addEdge('C', 'D'); 342 graph.addEdge('C', 'G'); 343 graph.addEdge('D', 'G'); 344 graph.addEdge('D', 'H'); 345 graph.addEdge('B', 'E'); 346 graph.addEdge('B', 'F'); 347 graph.addEdge('E', 'I'); 348 349 console.log('********* printing graph ***********'); 350 console.log(graph.toString()); 351 console.log('********* bfs with callback ***********'); 352 const printVertex = (value) => { 353 console.log('Visited vertex: ' + value) 354 } 355 breadthFirstSearch(graph, myVertices[0], printVertex); 356 console.log('********* shortest path - BFS ***********'); 357 const shortestPathA = BFS(graph, myVertices[0]); 358 console.log(shortestPathA.distances); 359 console.log(shortestPathA.predecessors); 360 361 const fromVertex = myVertices[0]; 362 for (let i = 1; i < myVertices.length; i++) { 363 const toVertex = myVertices[i]; 364 const path = new Stack(); 365 for(let v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) { 366 path.push(v); 367 } 368 path.push(fromVertex); 369 let s = path.pop(); 370 while (!path.isEmpty()) { 371 s += '-' + path.pop(); 372 } 373 console.log(s); 374 }
1.2 深度优先搜索 (算法工作原理的实现;构建森林;拓扑排序)
概念:深度优先搜索算法将会从一个指定的顶点开始遍历图,沿着路径直到这条路径最后一个顶点被访问了,接着原路回退并探索下一条路径(先深度后广度地访问顶点)。对于给定的图G,我们希望深度优先搜索算法遍历图G的所有节点,构建“森林”(有根树的一个集合)以及一组源顶点(根),并输出两个数组:发现时间和完成探索时间。而这些信息,我们可以用来做拓扑排序(当我们需要编排一些任务或者步骤的执行顺序时,这称为拓扑排序,其只能应用于有向无环图DAG)。
1 // import dictionary 2 class ValuePair { 3 constructor(key, value) { 4 this.key = key; 5 this.value = value; 6 } 7 8 toString() { 9 return `[#${this.key}: ${this.value}]`; 10 } 11 } 12 13 function defaultToString(item) { 14 if (item === null) { 15 return 'NULL'; 16 } if (item === undefined) { 17 return 'UNDEFINED'; 18 } if (typeof item === 'string' || item instanceof String) { 19 return `${item}`; 20 } 21 return item.toString(); 22 } 23 24 class Dictionary { 25 constructor(toStrFn = defaultToString) { 26 this.toStrFn = toStrFn; 27 this.table = {}; 28 } 29 hasKey(key) { 30 return this.table[this.toStrFn(key)] != null; 31 } 32 set(key, value) { 33 if (key != null && value != null) { 34 const tableKey = this.toStrFn(key); 35 this.table[tableKey] = new ValuePair(key,value); 36 return true; 37 } 38 return false; 39 } 40 remove(key) { 41 if (this.hasKey(key)) { 42 delete this.table[this.toStrFn(key)]; 43 return true; 44 } 45 return false; 46 } 47 get(key) { 48 const valuePair = this.table[this.toStrFn(key)]; 49 return valuePair == null ? undefined : valuePair.value; 50 } 51 keyValues() { 52 return Object.values(this.table); 53 } 54 values() { 55 return this.keyValues().map(valuePair => valuePair.value); 56 } 57 58 keys() { 59 return this.keyValues().map(valuePair => valuePair.key); 60 } 61 forEach(callbackFn) { 62 const valuePairs = this.keyValues(); 63 for (let i = 0; i < valuePairs.length; i++) { 64 const result = callbackFn(valuePairs[i].key, valuePairs[i].value); 65 if (result === false) { 66 break; 67 } 68 } 69 } 70 71 isEmpty() { 72 return this.size() === 0; 73 } 74 75 size() { 76 return Object.keys(this.table).length; 77 } 78 79 clear() { 80 this.table = {}; 81 } 82 83 toString() { 84 if (this.isEmpty()) { 85 return ''; 86 } 87 const valuePairs = this.keyValues(); 88 let objString = `${valuePairs[0].toString()}`; 89 for (let i = 1; i < valuePairs.length; i++) { 90 objString = `${objString},${valuePairs[i].toString()}`; 91 } 92 return objString; 93 } 94 95 96 } 97 98 class Graph { 99 constructor (isDirected = false) { 100 this.isDirected = isDirected; 101 this.vertices = []; 102 this.adjList = new Dictionary(); 103 } 104 addVertex(v) { 105 if (!this.vertices.includes(v)) { 106 this.vertices.push(v); 107 this.adjList.set(v, []); 108 } 109 } 110 addEdge(a, b ) { 111 if (!this.adjList.get(a)) { 112 this.addVertex(a); 113 } 114 if (!this.adjList.get(b)) { 115 this.adjList(b); 116 } 117 this.adjList.get(a).push(b); 118 if (this.isDirected !== true) { 119 this.adjList.get(b).push(a); 120 } 121 } 122 getVertices() { 123 return this.vertices; 124 } 125 getAdjList() { 126 return this.adjList; 127 } 128 toString() { 129 let s = ''; 130 for (let i = 0; i < this.vertices.length; i++) { 131 s += `${this.vertices[i]} -> `; 132 const neighbors = this.adjList.get(this.vertices[i]); 133 for (let j = 0; j < neighbors.length; j++) { 134 s +=`${neighbors[j]} `; 135 } 136 s += '\n'; 137 } 138 return s; 139 } 140 141 } 142 143 144 145 const Colors = { 146 WHITE: 0, 147 GREY: 1, 148 BLACK: 2 149 }; 150 151 152 const initializeColor = vertices => { 153 const color = {}; 154 for (let i = 0; i < vertices.length; i++) { 155 color[vertices[i]] = Colors.WHITE; 156 } 157 return color; 158 }; 159 160 const depthFirstSearchVisit = (u, color, adjList, callback) =>{ 161 color[u] = Colors.GREY; 162 if (callback) { 163 callback(u); 164 } 165 const neighbors = adjList.get(u); 166 for (let i = 0; i < neighbors.length; i++) { 167 const w = neighbors[i]; 168 if (color[w] === Colors.WHITE) { 169 depthFirstSearchVisit(w, color, adjList, callback); 170 } 171 } 172 color[u] = Colors.BLACK; 173 }; 174 175 const depthFirstSearch = (graph, callback) =>{ 176 const vertices = graph.getVertices(); 177 const adjList = graph.getAdjList(); 178 const color = initializeColor(vertices); 179 for (let i = 0; i < vertices.length; i++) { 180 if (color[vertices[i]] === Colors.WHITE) { 181 depthFirstSearchVisit(vertices[i], color, adjList, callback); 182 } 183 } 184 185 }; 186 187 const DFSVisit = (u, color, d, f, p, time, adjList) => { 188 color[u] = Colors.GREY; 189 d[u] = ++time.count; 190 const neighbors = adjList.get(u); 191 for (let i =0; i <neighbors.length; i++) { 192 const w = neighbors[i]; 193 if (color[w] === Colors.WHITE) { 194 p[w] = u; 195 DFSVisit(w, color, d, f, p, time, adjList); 196 } 197 } 198 color[u] = Colors.BLACK; 199 f[u] = ++time.count; 200 }; 201 202 const DFS = graph =>{ 203 const vertices = graph.getVertices(); 204 const adjList = graph.getAdjList(); 205 const color = initializeColor(vertices); 206 const d = {}; 207 const f = {}; 208 const p = {}; 209 const time = {count: 0}; 210 for (let i = 0; i < vertices.length; i++) { 211 f[vertices[i]] = 0; 212 d[vertices[i]] = 0; 213 p[vertices[i]] = null; 214 } 215 for (let i = 0; i < vertices.length; i++) { 216 if (color[vertices[i]] === Colors.WHITE) { 217 DFSVisit(vertices[i], color, d, f, p, time, adjList); 218 } 219 } 220 return { 221 discovery: d, 222 finished: f, 223 predecessors:p 224 }; 225 226 }; 227 228 229 230 let graph = new Graph(true); 231 232 let myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']; 233 234 for (let i = 0; i < myVertices.length; i++) { 235 graph.addVertex(myVertices[i]); 236 } 237 graph.addEdge('A', 'B'); 238 graph.addEdge('A', 'C'); 239 graph.addEdge('A', 'D'); 240 graph.addEdge('C', 'D'); 241 graph.addEdge('C', 'G'); 242 graph.addEdge('D', 'G'); 243 graph.addEdge('D', 'H'); 244 graph.addEdge('B', 'E'); 245 graph.addEdge('B', 'F'); 246 graph.addEdge('E', 'I'); 247 248 console.log('********* printing graph ***********'); 249 250 console.log(graph.toString()); 251 252 console.log('********* dfs with callback ***********'); 253 254 const printVertex = value => console.log('Visited vertex: ' + value); 255 256 depthFirstSearch(graph, printVertex); 257 console.log('********* DFS ***********'); 258 259 const result = DFS(graph); 260 console.log('discovery', result.discovery); 261 console.log('finished', result.finished); 262 console.log('predecessors', result.predecessors); 263 264 console.log('********* topological sort - DFS ***********'); 265 266 graph = new Graph(true); 267 268 myVertices = ['A', 'B', 'C', 'D', 'E', 'F']; 269 for (i = 0; i < myVertices.length; i++) { 270 graph.addVertex(myVertices[i]); 271 } 272 graph.addEdge('A', 'C'); 273 graph.addEdge('A', 'D'); 274 graph.addEdge('B', 'D'); 275 graph.addEdge('B', 'E'); 276 graph.addEdge('C', 'F'); 277 graph.addEdge('F', 'E'); 278 279 const result2 = DFS(graph); 280 console.log('discovery', result.discovery); 281 console.log('finished', result.finished); 282 console.log('predecessors', result.predecessors); 283 284 const fTimes = result2.finished; 285 s = ''; 286 for (let count = 0; count < myVertices.length; count ++) { 287 let max = 0; 288 let maxName = null; 289 for (i = 0; i < myVertices.length; i++) { 290 if (fTimes[myVertices[i]] > max ) { 291 max = fTimes[myVertices[i]]; 292 maxName = myVertices[i]; 293 } 294 } 295 s += ' - ' + maxName; 296 delete fTimes[maxName]; 297 } 298 console.log(s);
1.3 最短路径算法(Dijkstra算法、Floyd-Warshall算法)
1.3.1 Dijkstra算法
概念:Dijkstra算法是一种计算从单个源到所有其他源的最短路径的贪心算法,这意味着我们可以用它来计算从图的一个顶点到其余各顶点的最短路径。
1 const INF = Number.MAX_SAFE_INTEGER; 2 const minDistance = (dist, visited) => { 3 let min = INF; 4 let minIndex = -1; 5 for (let v = 0; v < dist.length; v++) { 6 if (visited[v] === false && dist[v] <= min) { 7 min = dist[v]; 8 minIndex = v; 9 } 10 } 11 return minIndex; 12 }; 13 14 const dijkstra = (graph, src) => { 15 const dist = []; 16 const visited = []; 17 const {length} = graph; 18 for (let i = 0; i < length; i++ ) { 19 dist[i] = INF; 20 visited[i] = false; 21 } 22 dist[src] = 0; 23 for (let i = 0; i < length -1; i++) { 24 const u = minDistance(dist, visited); 25 visited[u] = true; 26 for (let v = 0; v < length; v++) { 27 if (!visited[v] && graph[u][v] !== 0 && dist[u] !== INF && dist[u] + graph[u][v] < dist[v]) { 28 dist [v] = dist[u] + graph[u][v]; 29 } 30 } 31 } 32 return dist; 33 }; 34 35 36 const graph = [ 37 [0, 2, 4, 0, 0, 0], 38 [0, 0, 2, 4, 2, 0], 39 [0, 0, 0, 0, 3, 0], 40 [0, 0, 0, 0, 0, 2], 41 [0, 0, 0, 3, 0, 2], 42 [0, 0, 0, 0, 0, 0] 43 ]; 44 45 console.log("********* Dijkstra's Algorithm - Shortest Path ***********"); 46 47 const dist = dijkstra(graph, 0); 48 49 for (i = 0; i < dist.length; i++){ 50 console.log(i + '\t\t' + dist[i]); 51 }
1.3.2 Floyd-Warshall算法
概念:Floyd-Warshall算法是一种计算图中所有最短路径的动态规划算法。通过该算法,我们可以找出从所有源到所有顶点的最短路径。
1 const floydWarshall = graph => { 2 const dist = []; 3 const {length} = graph; 4 for (let i = 0; i < length; i++) { 5 dist[i] = []; 6 for (let j = 0; j < length; j++) { 7 if (i === j) { 8 dist[i][j] = 0; 9 } else if (!isFinite(graph[i][j])) { 10 dist[i][j] = Infinity; 11 } else { 12 dist[i][j] = graph[i][j]; 13 } 14 } 15 } 16 for (let k = 0; k < length; k++) { 17 for(let i = 0; i < length; i++) { 18 for (let j = 0; j < length; j++) { 19 if (dist[i][k] + dist[k][j] < dist[i][j]) { 20 dist[i][j] = dist[i][k] + dist[k][j]; 21 } 22 } 23 } 24 } 25 return dist; 26 }; 27 28 const INF = Infinity; 29 const graph = [ 30 [INF, 2, 4, INF, INF, INF], 31 [INF, INF, 2, 4, 2, INF], 32 [INF, INF, INF, INF, 3, INF], 33 [INF, INF, INF, INF, INF, 2], 34 [INF, INF, INF, 3, INF, 2], 35 [INF, INF, INF, INF, INF, INF] 36 ]; 37 38 dist = floydWarshall(graph); 39 let s = ''; 40 for (let i = 0; i < dist.length; i++) { 41 s = ''; 42 for (let j = 0; j < dist.length; j++) { 43 if (dist[i][j] === INF) { 44 s += 'INF '; 45 } else { 46 s += dist[i][j] + ' '; 47 } 48 49 } 50 console.log(s); 51 }
1.4 最小生成树(Prim算法、Kruskal算法)
概念:最小生成树(MST,最小权重生成树)是一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树问题是网络设计中常见的问题,例如办公室电话线路通信问题和岛桥问题。
1.4.1 Prim算法(“加点法”)
概念:Prim算法是一种求解加权无向连通图的MST问题的贪心算法。它能找到一个边的子集,使得其构成的树包含图中所有顶点,并且边的权值之和最小。
1 const INF = Number.MAX_SAFE_INTEGER; 2 const minKey = (graph, key, visited) => { 3 let min = INF; 4 let minIndex = 0; 5 for (let v = 0; v < graph.length; v++) { 6 if (visited[v] === false && key[v] < min) { 7 min = key[v]; 8 minIndex = v; 9 } 10 } 11 return minIndex; 12 }; 13 14 const prim = graph => { 15 const parent = []; 16 const key = []; 17 const visited = []; 18 const {length} = graph; 19 for (let i = 0; i < length; i++) { 20 key[i] = INF; 21 visited[i] = false; 22 } 23 key[0] = 0; 24 parent[0] = -1; 25 for (let i = 0; i < length - 1; i++) { 26 const u = minKey(graph, key, visited); 27 visited[u] = true; 28 for (let v = 0; v < length; v++) { 29 if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) { 30 parent[v] = u; 31 key[v] = graph[u][v]; 32 } 33 } 34 } 35 return parent; 36 }; 37 38 const graph = [ 39 [0, 2, 4, 0, 0, 0], 40 [2, 0, 2, 4, 2, 0], 41 [4, 2, 0, 0, 3, 0], 42 [0, 4, 0, 0, 3, 2], 43 [0, 2, 3, 3, 0, 2], 44 [0, 0, 0, 2, 2, 0] 45 ]; 46 47 48 49 const parent = prim(graph); 50 51 console.log('Edge Weight'); 52 for (let i = 1; i < graph.length; i++) { 53 console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); 54 }
1.4.2 Kruskal算法(“加边法”)
概念:Kruskal算法也是一种求加权无向连通图的MST的贪心算法。
1 const INF = Number.MAX_SAFE_INTEGER; 2 const find = (i, parent) => { 3 while (parent[i]) { 4 i = parent[i]; 5 } 6 return i; 7 }; 8 9 const union = (i, j, parent) => { 10 if (i !== j) { 11 parent[j] = i; 12 return true; 13 } 14 return false; 15 }; 16 const initializeCost = graph => { 17 const cost = []; 18 const {length} = graph; 19 for (let i = 0; i < length; i++) { 20 cost[i] =[]; 21 for (let j = 0; j < length; j++) { 22 if (graph[i][j] === 0) { 23 cost[i][j] = INF; 24 } else { 25 cost[i][j] = graph[i][j]; 26 } 27 } 28 } 29 return cost; 30 }; 31 32 const kruskal = graph => { 33 const {length} = graph; 34 const parent = []; 35 let ne = 0; 36 let a; 37 let b; 38 let u; 39 let v; 40 const cost = initializeCost(graph); 41 while (ne < length - 1) { 42 for (let i = 0, min = INF; i < length; i++) { 43 for (let j = 0; j < length; j++) { 44 if (cost[i][j] < min) { 45 min = cost[i][j]; 46 a = u = i; 47 b = v = j; 48 } 49 } 50 } 51 u = find(u, parent); 52 v = find(v, parent); 53 if (union(u, v, parent)) { 54 ne++; 55 } 56 cost[a][b] = cost[b][a] = INF; 57 } 58 return parent; 59 }; 60 61 62 const graph = [ 63 [0, 2, 4, 0, 0, 0], 64 [2, 0, 2, 4, 2, 0], 65 [4, 2, 0, 0, 3, 0], 66 [0, 4, 0, 0, 3, 2], 67 [0, 2, 3, 3, 0, 2], 68 [0, 0, 0, 2, 2, 0] 69 ]; 70 71 72 73 const parent = kruskal(graph); 74 75 console.log('Edge Weight'); 76 for (i = 1; i < graph.length; i++) { 77 console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); 78 }