目的:ES6标准下的JS数据结构的一些实现代码。(作为记录和启发)

内容:图,图的遍历(广度优先搜索BFS,深度优先搜索DFS),最短路径算法,最小生成树算法。(未完成,待继续)

所有源码在我的Github上(如果觉得不错记得给星鼓励我哦):ES6的JavaScript数据结构实现之图

一、基础数据结构

1、图(创建Graph类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// import dictionary
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}

toString() {
return `[#${this.key}: ${this.value}]`;
}
}

function defaultToString(item) {
if (item === null) {
return 'NULL';
} if (item === undefined) {
return 'UNDEFINED';
} if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}

class Dictionary {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key);
this.table[tableKey] = new ValuePair(key,value);
return true;
}
return false;
}
remove(key) {
if (this.hasKey(key)) {
delete this.table[this.toStrFn(key)];
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.toStrFn(key)];
return valuePair == null ? undefined : valuePair.value;
}
keyValues() {
return Object.values(this.table);
}
values() {
return this.keyValues().map(valuePair => valuePair.value);
}

keys() {
return this.keyValues().map(valuePair => valuePair.key);
}
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i++) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}

isEmpty() {
return this.size() === 0;
}

size() {
return Object.keys(this.table).length;
}

clear() {
this.table = {};
}

toString() {
if (this.isEmpty()) {
return '';
}
const valuePairs = this.keyValues();
let objString = `${valuePairs[0].toString()}`;
for (let i = 1; i < valuePairs.length; i++) {
objString = `${objString},${valuePairs[i].toString()}`;
}
return objString;
}


}

class Graph {
constructor (isDirected = false) {
this.isDirected = isDirected;
this.vertices = [];
this.adjList = new Dictionary();
}
addVertex(v) {
if (!this.vertices.includes(v)) {
this.vertices.push(v);
this.adjList.set(v, []);
}
}
addEdge(a, b ) {
if (!this.adjList.get(a)) {
this.addVertex(a);
}
if (!this.adjList.get(b)) {
this.adjList(b);
}
this.adjList.get(a).push(b);
if (this.isDirected !== true) {
this.adjList.get(b).push(a);
}
}
getVertices() {
return this.vertices;
}
getAdjList() {
return this.adjList;
}
toString() {
let s = '';
for (let i = 0; i < this.vertices.length; i++) {
s += `${this.vertices[i]} -> `;
const neighbors = this.adjList.get(this.vertices[i]);
for (let j = 0; j < neighbors.length; j++) {
s +=`${neighbors[j]} `;
}
s += '\n';
}
return s;
}

}


const graph = new Graph();
const myVertices = ['A','B','C','D','E','F','G','H','I'];
for (let i = 0; i < myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
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());

Graph

二、简单应用

1、图的遍历

1.1 广度优先搜索(算法工作原理实现;使用BFS寻找最短路径)

概念:广度优先搜索算法会从指定的第一个顶点开始遍历图,先访问其所有的邻点(相邻顶点),就像一次访问图的一层(先宽后深地访问顶点)。使用BFS寻找最短路径(给定一个图G和源顶点v,找出每个顶点u和v之间最短路径的距离(以边的数量计))。

注:在上面的最短路径算法中,图不是加权的。如果要计算加权图中的最短路径,广度优先搜索未必合适,要考虑使用Dijkstra算法、Floyd-Warshell算法、Bellman-Ford算法、A*搜索算法(前两者的实现在本博客的最后)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
// import dictionary
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}

toString() {
return `[#${this.key}: ${this.value}]`;
}
}

function defaultToString(item) {
if (item === null) {
return 'NULL';
} if (item === undefined) {
return 'UNDEFINED';
} if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}

class Dictionary {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key);
this.table[tableKey] = new ValuePair(key,value);
return true;
}
return false;
}
remove(key) {
if (this.hasKey(key)) {
delete this.table[this.toStrFn(key)];
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.toStrFn(key)];
return valuePair == null ? undefined : valuePair.value;
}
keyValues() {
return Object.values(this.table);
}
values() {
return this.keyValues().map(valuePair => valuePair.value);
}

keys() {
return this.keyValues().map(valuePair => valuePair.key);
}
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i++) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}

isEmpty() {
return this.size() === 0;
}

size() {
return Object.keys(this.table).length;
}

clear() {
this.table = {};
}

toString() {
if (this.isEmpty()) {
return '';
}
const valuePairs = this.keyValues();
let objString = `${valuePairs[0].toString()}`;
for (let i = 1; i < valuePairs.length; i++) {
objString = `${objString},${valuePairs[i].toString()}`;
}
return objString;
}


}

class Graph {
constructor (isDirected = false) {
this.isDirected = isDirected;
this.vertices = [];
this.adjList = new Dictionary();
}
addVertex(v) {
if (!this.vertices.includes(v)) {
this.vertices.push(v);
this.adjList.set(v, []);
}
}
addEdge(a, b ) {
if (!this.adjList.get(a)) {
this.addVertex(a);
}
if (!this.adjList.get(b)) {
this.adjList(b);
}
this.adjList.get(a).push(b);
if (this.isDirected !== true) {
this.adjList.get(b).push(a);
}
}
getVertices() {
return this.vertices;
}
getAdjList() {
return this.adjList;
}
toString() {
let s = '';
for (let i = 0; i < this.vertices.length; i++) {
s += `${this.vertices[i]} -> `;
const neighbors = this.adjList.get(this.vertices[i]);
for (let j = 0; j < neighbors.length; j++) {
s +=`${neighbors[j]} `;
}
s += '\n';
}
return s;
}

}
//import Queue
class Queue{
constructor(){
this.count = 0;
this.lowestCount = 0;
this.items ={};
}


enqueue(element) {
this.items[this.count] = element;
this.count++;
}

dequeue() {
if (this.isEmpty()) {
return undefined;
}
const result = this.items[this.lowestCount];
delete this.items[this.lowestCount];
this.lowestCount++;
return result;
}

peek() {
if (this.isEmpty()){
return undefined;
}
return this.items[this.lowestCount];
}

isEmpty() {
return this.size() === 0;
}
size() {
return this.count - this.lowestCount;
}
clear(){
this.items = {};
this.count = 0;
this.lowestCount = 0;
}
toString(){
if (this.isEmpty()){
return '';
}
let objString = `${this.items[this.lowestCount]}`
for (let i= this.lowestCount + 1; i < this.count;i++){
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}

// stack for shortest path - BFS
class Stack {
constructor() {
this.count = 0;
this.items = {};
}

push(element) {
this.items[this.count] = element;
this.count++;
}

pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}

peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}

isEmpty() {
return this.count === 0;
}

size() {
return this.count;
}

clear() {
/* while (!this.isEmpty()) {
this.pop();
} */
this.items = {};
this.count = 0;
}

toString() {
if (this.isEmpty()) {
return '';
}
let objString = `${this.items[0]}`;
for (let i = 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}


const Colors = {
WHITE: 0,
GREY: 1,
BLACK: 2
};


const initializeColor = vertices => {
const color = {};
for (let i = 0; i < vertices.length; i++) {
color[vertices[i]] = Colors.WHITE;
}
return color;
};

const breadthFirstSearch = (graph, startVertex, callback) => {
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices);
const queue = new Queue;

queue.enqueue(startVertex);
while (!queue.isEmpty()) {
const u = queue.dequeue();
const neighbors = adjList.get(u);
color[u] = Colors.GREY;
for (let i = 0; i < neighbors.length; i++) {
const w = neighbors[i];
if (color[w] === Colors.WHITE) {
color[w] = Colors.GREY;
queue.enqueue(w);
}
}
color[u] = Colors.BLACK;
if (callback) {
callback(u);
}

}

};

const BFS = (graph, startVertex) => {
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices);
const queue = new Queue;
const distances = {};
const predecessors = {};
queue.enqueue(startVertex);

for (let i = 0; i < vertices.length; i++) {
distances[vertices[i]] = 0;
predecessors[vertices[i]] = null;
}
while (!queue.isEmpty()) {
const u = queue.dequeue();
const neighbors = adjList.get(u);
color[u] = Colors.GREY;
for (let i = 0; i < neighbors.length; i++) {
const w = neighbors[i];
if (color[w] === Colors.WHITE) {
color[w] = Colors.GREY;
distances[w] = distances[u] + 1;
predecessors[w] = u;
queue.enqueue(w);
}
}
color[u] = Colors.BLACK;
}
return {
distances,
predecessors
};
};


const graph = new Graph();

const myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];

for (let i = 0; i < myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
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('********* printing graph ***********');
console.log(graph.toString());
console.log('********* bfs with callback ***********');
const printVertex = (value) => {
console.log('Visited vertex: ' + value)
}
breadthFirstSearch(graph, myVertices[0], printVertex);
console.log('********* shortest path - BFS ***********');
const shortestPathA = BFS(graph, myVertices[0]);
console.log(shortestPathA.distances);
console.log(shortestPathA.predecessors);

const fromVertex = myVertices[0];
for (let i = 1; i < myVertices.length; i++) {
const toVertex = myVertices[i];
const path = new Stack();
for(let v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) {
path.push(v);
}
path.push(fromVertex);
let s = path.pop();
while (!path.isEmpty()) {
s += '-' + path.pop();
}
console.log(s);
}

BFS
1.2 深度优先搜索 (算法工作原理的实现;构建森林;拓扑排序)

概念:深度优先搜索算法将会从一个指定的顶点开始遍历图,沿着路径直到这条路径最后一个顶点被访问了,接着原路回退并探索下一条路径(先深度后广度地访问顶点)。对于给定的图G,我们希望深度优先搜索算法遍历图G的所有节点,构建“森林”(有根树的一个集合)以及一组源顶点(根),并输出两个数组:发现时间和完成探索时间。而这些信息,我们可以用来做拓扑排序(当我们需要编排一些任务或者步骤的执行顺序时,这称为拓扑排序,其只能应用于有向无环图DAG)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// import dictionary
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}

toString() {
return `[#${this.key}: ${this.value}]`;
}
}

function defaultToString(item) {
if (item === null) {
return 'NULL';
} if (item === undefined) {
return 'UNDEFINED';
} if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString();
}

class Dictionary {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key);
this.table[tableKey] = new ValuePair(key,value);
return true;
}
return false;
}
remove(key) {
if (this.hasKey(key)) {
delete this.table[this.toStrFn(key)];
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.toStrFn(key)];
return valuePair == null ? undefined : valuePair.value;
}
keyValues() {
return Object.values(this.table);
}
values() {
return this.keyValues().map(valuePair => valuePair.value);
}

keys() {
return this.keyValues().map(valuePair => valuePair.key);
}
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i++) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}

isEmpty() {
return this.size() === 0;
}

size() {
return Object.keys(this.table).length;
}

clear() {
this.table = {};
}

toString() {
if (this.isEmpty()) {
return '';
}
const valuePairs = this.keyValues();
let objString = `${valuePairs[0].toString()}`;
for (let i = 1; i < valuePairs.length; i++) {
objString = `${objString},${valuePairs[i].toString()}`;
}
return objString;
}


}

class Graph {
constructor (isDirected = false) {
this.isDirected = isDirected;
this.vertices = [];
this.adjList = new Dictionary();
}
addVertex(v) {
if (!this.vertices.includes(v)) {
this.vertices.push(v);
this.adjList.set(v, []);
}
}
addEdge(a, b ) {
if (!this.adjList.get(a)) {
this.addVertex(a);
}
if (!this.adjList.get(b)) {
this.adjList(b);
}
this.adjList.get(a).push(b);
if (this.isDirected !== true) {
this.adjList.get(b).push(a);
}
}
getVertices() {
return this.vertices;
}
getAdjList() {
return this.adjList;
}
toString() {
let s = '';
for (let i = 0; i < this.vertices.length; i++) {
s += `${this.vertices[i]} -> `;
const neighbors = this.adjList.get(this.vertices[i]);
for (let j = 0; j < neighbors.length; j++) {
s +=`${neighbors[j]} `;
}
s += '\n';
}
return s;
}

}



const Colors = {
WHITE: 0,
GREY: 1,
BLACK: 2
};


const initializeColor = vertices => {
const color = {};
for (let i = 0; i < vertices.length; i++) {
color[vertices[i]] = Colors.WHITE;
}
return color;
};

const depthFirstSearchVisit = (u, color, adjList, callback) =>{
color[u] = Colors.GREY;
if (callback) {
callback(u);
}
const neighbors = adjList.get(u);
for (let i = 0; i < neighbors.length; i++) {
const w = neighbors[i];
if (color[w] === Colors.WHITE) {
depthFirstSearchVisit(w, color, adjList, callback);
}
}
color[u] = Colors.BLACK;
};

const depthFirstSearch = (graph, callback) =>{
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices);
for (let i = 0; i < vertices.length; i++) {
if (color[vertices[i]] === Colors.WHITE) {
depthFirstSearchVisit(vertices[i], color, adjList, callback);
}
}

};

const DFSVisit = (u, color, d, f, p, time, adjList) => {
color[u] = Colors.GREY;
d[u] = ++time.count;
const neighbors = adjList.get(u);
for (let i =0; i <neighbors.length; i++) {
const w = neighbors[i];
if (color[w] === Colors.WHITE) {
p[w] = u;
DFSVisit(w, color, d, f, p, time, adjList);
}
}
color[u] = Colors.BLACK;
f[u] = ++time.count;
};

const DFS = graph =>{
const vertices = graph.getVertices();
const adjList = graph.getAdjList();
const color = initializeColor(vertices);
const d = {};
const f = {};
const p = {};
const time = {count: 0};
for (let i = 0; i < vertices.length; i++) {
f[vertices[i]] = 0;
d[vertices[i]] = 0;
p[vertices[i]] = null;
}
for (let i = 0; i < vertices.length; i++) {
if (color[vertices[i]] === Colors.WHITE) {
DFSVisit(vertices[i], color, d, f, p, time, adjList);
}
}
return {
discovery: d,
finished: f,
predecessors:p
};

};



let graph = new Graph(true);

let myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];

for (let i = 0; i < myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
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('********* printing graph ***********');

console.log(graph.toString());

console.log('********* dfs with callback ***********');

const printVertex = value => console.log('Visited vertex: ' + value);

depthFirstSearch(graph, printVertex);
console.log('********* DFS ***********');

const result = DFS(graph);
console.log('discovery', result.discovery);
console.log('finished', result.finished);
console.log('predecessors', result.predecessors);

console.log('********* topological sort - DFS ***********');

graph = new Graph(true);

myVertices = ['A', 'B', 'C', 'D', 'E', 'F'];
for (i = 0; i < myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
graph.addEdge('A', 'C');
graph.addEdge('A', 'D');
graph.addEdge('B', 'D');
graph.addEdge('B', 'E');
graph.addEdge('C', 'F');
graph.addEdge('F', 'E');

const result2 = DFS(graph);
console.log('discovery', result.discovery);
console.log('finished', result.finished);
console.log('predecessors', result.predecessors);

const fTimes = result2.finished;
s = '';
for (let count = 0; count < myVertices.length; count ++) {
let max = 0;
let maxName = null;
for (i = 0; i < myVertices.length; i++) {
if (fTimes[myVertices[i]] > max ) {
max = fTimes[myVertices[i]];
maxName = myVertices[i];
}
}
s += ' - ' + maxName;
delete fTimes[maxName];
}
console.log(s);

DFS
1.3 最短路径算法(Dijkstra算法、Floyd-Warshall算法)

1.3.1 Dijkstra算法

概念:Dijkstra算法是一种计算从单个源到所有其他源的最短路径的贪心算法,这意味着我们可以用它来计算从图的一个顶点到其余各顶点的最短路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const INF = Number.MAX_SAFE_INTEGER;
const minDistance = (dist, visited) => {
let min = INF;
let minIndex = -1;
for (let v = 0; v < dist.length; v++) {
if (visited[v] === false && dist[v] <= min) {
min = dist[v];
minIndex = v;
}
}
return minIndex;
};

const dijkstra = (graph, src) => {
const dist = [];
const visited = [];
const {length} = graph;
for (let i = 0; i < length; i++ ) {
dist[i] = INF;
visited[i] = false;
}
dist[src] = 0;
for (let i = 0; i < length -1; i++) {
const u = minDistance(dist, visited);
visited[u] = true;
for (let v = 0; v < length; v++) {
if (!visited[v] && graph[u][v] !== 0 && dist[u] !== INF && dist[u] + graph[u][v] < dist[v]) {
dist [v] = dist[u] + graph[u][v];
}
}
}
return dist;
};


const graph = [
[0, 2, 4, 0, 0, 0],
[0, 0, 2, 4, 2, 0],
[0, 0, 0, 0, 3, 0],
[0, 0, 0, 0, 0, 2],
[0, 0, 0, 3, 0, 2],
[0, 0, 0, 0, 0, 0]
];

console.log("********* Dijkstra's Algorithm - Shortest Path ***********");

const dist = dijkstra(graph, 0);

for (i = 0; i < dist.length; i++){
console.log(i + '\t\t' + dist[i]);
}

Dijkstra

1.3.2 Floyd-Warshall算法

概念:Floyd-Warshall算法是一种计算图中所有最短路径的动态规划算法。通过该算法,我们可以找出从所有源到所有顶点的最短路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const floydWarshall = graph => {
const dist = [];
const {length} = graph;
for (let i = 0; i < length; i++) {
dist[i] = [];
for (let j = 0; j < length; j++) {
if (i === j) {
dist[i][j] = 0;
} else if (!isFinite(graph[i][j])) {
dist[i][j] = Infinity;
} else {
dist[i][j] = graph[i][j];
}
}
}
for (let k = 0; k < length; k++) {
for(let i = 0; i < length; i++) {
for (let j = 0; j < length; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
return dist;
};

const INF = Infinity;
const graph = [
[INF, 2, 4, INF, INF, INF],
[INF, INF, 2, 4, 2, INF],
[INF, INF, INF, INF, 3, INF],
[INF, INF, INF, INF, INF, 2],
[INF, INF, INF, 3, INF, 2],
[INF, INF, INF, INF, INF, INF]
];

dist = floydWarshall(graph);
let s = '';
for (let i = 0; i < dist.length; i++) {
s = '';
for (let j = 0; j < dist.length; j++) {
if (dist[i][j] === INF) {
s += 'INF ';
} else {
s += dist[i][j] + ' ';
}

}
console.log(s);
}

floydWarshall
1.4 最小生成树(Prim算法、Kruskal算法)

概念:最小生成树(MST,最小权重生成树)是一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树问题是网络设计中常见的问题,例如办公室电话线路通信问题和岛桥问题。

1.4.1 Prim算法(“加点法”)

概念:Prim算法是一种求解加权无向连通图的MST问题的贪心算法。它能找到一个边的子集,使得其构成的树包含图中所有顶点,并且边的权值之和最小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const INF = Number.MAX_SAFE_INTEGER;
const minKey = (graph, key, visited) => {
let min = INF;
let minIndex = 0;
for (let v = 0; v < graph.length; v++) {
if (visited[v] === false && key[v] < min) {
min = key[v];
minIndex = v;
}
}
return minIndex;
};

const prim = graph => {
const parent = [];
const key = [];
const visited = [];
const {length} = graph;
for (let i = 0; i < length; i++) {
key[i] = INF;
visited[i] = false;
}
key[0] = 0;
parent[0] = -1;
for (let i = 0; i < length - 1; i++) {
const u = minKey(graph, key, visited);
visited[u] = true;
for (let v = 0; v < length; v++) {
if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}
return parent;
};

const graph = [
[0, 2, 4, 0, 0, 0],
[2, 0, 2, 4, 2, 0],
[4, 2, 0, 0, 3, 0],
[0, 4, 0, 0, 3, 2],
[0, 2, 3, 3, 0, 2],
[0, 0, 0, 2, 2, 0]
];



const parent = prim(graph);

console.log('Edge Weight');
for (let i = 1; i < graph.length; i++) {
console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]);
}

prim

1.4.2 Kruskal算法(“加边法”)

概念:Kruskal算法也是一种求加权无向连通图的MST的贪心算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const INF = Number.MAX_SAFE_INTEGER;
const find = (i, parent) => {
while (parent[i]) {
i = parent[i];
}
return i;
};

const union = (i, j, parent) => {
if (i !== j) {
parent[j] = i;
return true;
}
return false;
};
const initializeCost = graph => {
const cost = [];
const {length} = graph;
for (let i = 0; i < length; i++) {
cost[i] =[];
for (let j = 0; j < length; j++) {
if (graph[i][j] === 0) {
cost[i][j] = INF;
} else {
cost[i][j] = graph[i][j];
}
}
}
return cost;
};

const kruskal = graph => {
const {length} = graph;
const parent = [];
let ne = 0;
let a;
let b;
let u;
let v;
const cost = initializeCost(graph);
while (ne < length - 1) {
for (let i = 0, min = INF; i < length; i++) {
for (let j = 0; j < length; j++) {
if (cost[i][j] < min) {
min = cost[i][j];
a = u = i;
b = v = j;
}
}
}
u = find(u, parent);
v = find(v, parent);
if (union(u, v, parent)) {
ne++;
}
cost[a][b] = cost[b][a] = INF;
}
return parent;
};


const graph = [
[0, 2, 4, 0, 0, 0],
[2, 0, 2, 4, 2, 0],
[4, 2, 0, 0, 3, 0],
[0, 4, 0, 0, 3, 2],
[0, 2, 3, 3, 0, 2],
[0, 0, 0, 2, 2, 0]
];



const parent = kruskal(graph);

console.log('Edge Weight');
for (i = 1; i < graph.length; i++) {
console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]);
}

kruskal