-
Notifications
You must be signed in to change notification settings - Fork 4
/
Graph.js
391 lines (311 loc) · 8.5 KB
/
Graph.js
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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 19:49:19
* @Desc: 图
*/
/*
图的定义
图由"边"的集合以及"顶点"的集合组成。
边:由顶点对(顶点1,顶点2)定义。
顶点:有权重,也称为成本。
有序图:图的顶点是有序的,称之为有向图,有向图表明了顶点的流向。
无序图:图是无序的。
路径:由一系列顶点构成,路径中的所有顶点都由边链接。
路径的长度:用路径中的第一个顶点到最后一个顶点之间边的数量表示。
环:由指向自身顶点组成的路径称为环,环的长度称为0。
圈:至少有一条边的路径,且路径的第一个顶点和最后一个顶点相同。
简单圈:没有重复边或重复顶点的圈。
平凡圈:除了第一个和最后一个顶点外,路径的其他顶点有重复的圈。
*/
/*
图建模
交通流量建模:顶点可以表示街道的十字路口,边可以表示街道。
加权的边可以表示限速或者车道的数量,建模人员可以用这个系统来判最佳路线以及最有可能堵车的街道。
任何运输系统都可以用图来建模。例如航空公司可以用图来为飞行系统建模。将每个机场看成顶点,将经过两个顶点的每条航线看作一条边。
加权的边可以表示从一个机场到另一个机场的航班成本,或两个机场的距离,这取决于建模的对象是什么。
任何计算器网络同样经常用图来建模。
*/
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 20:33:48
* @Desc: 图类
* @Parm:
*/
function Graph(v) {
// 顶点数
this.vertices = v
// 边数
this.edges = 0
this.adj = []
for(let i=0; i<this.vertices; i++) {
this.adj[i] = []
}
}
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 20:35:37
* @Desc: 添加边
* @Parm:
*/
Graph.prototype.addEdge = function(v, w) {
this.adj[v].push(w)
this.adj[w].push(v)
this.edges ++
}
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 20:38:47
* @Desc: 显示图
* @Parm:
*/
Graph.prototype.show = function() {
for(let i=0; i<this.vertices; i++) {
console.log('i -> ', i)
for(j=0; j<this.vertices; j++) {
if(this.adj[i][j] !== undefined) {
console.log(this.adj[i][j])
}
}
}
}
let g = new Graph(10)
console.log(g)
g.addEdge(0,1)
g.addEdge(1,3)
console.log(g.show())
/*
图搜索
深度优先搜索:
从一条路径的起始顶点开始追溯,直到到达最后一个顶点,然后回溯,继续追溯下一条路径,知道到达最后一个顶点,如此往复,直到没有路径为止。
这不是搜索特定的路径,而是通过搜索来查看图中有哪些路径可以选择。
*/
/**
* @Author: zhuxiankang
* @Date: 2018-10-17 09:23:17
* @Desc: 深度优先搜索
* @Parm:
*/
function DeepFirstGraph(v) {
// 顶点数
this.vertices = v
// 边数
this.edges = 0
this.adj = []
// 是否被访问
this.marked = []
for(var i=0; i<this.vertices; i++) {
this.adj[i] = []
this.marked[i] = false
}
}
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 20:35:37
* @Desc: 添加边
* @Parm:
*/
DeepFirstGraph.prototype.addEdge = function(v, w) {
this.adj[v].push(w)
this.adj[w].push(v)
this.edges ++
}
/**
* @Author: zhuxiankang
* @Date: 2018-10-17 08:51:58
* @Desc: 深度优先搜索
* @Parm:
*/
DeepFirstGraph.prototype.deepFirstSearch = function(v) {
// 表明当前顶点被访问过
this.marked[v] = true
if(!this.adj[v] === undefined) return
// 当前被访问的顶点
console.log('visited: ', v)
// 遍历和顶点v有边链接的其他顶点
for(let w of this.adj[v]) {
// 如果当前顶点没有被访问过,则继续访问
if(!this.marked[w]) {
this.deepFirstSearch(w)
}
}
}
/**
* @Author: zhuxiankang
* @Date: 2018-10-16 20:38:47
* @Desc: 显示图
* @Parm:
*/
DeepFirstGraph.prototype.show = function() {
for(let i=0; i<this.vertices; i++) {
console.log('i -> ', i)
for(j=0; j<this.vertices; j++) {
if(this.adj[i][j] !== undefined) {
console.log(this.adj[i][j])
}
}
}
}
let deepFirstGraph = new DeepFirstGraph(6)
deepFirstGraph.addEdge(0,1)
deepFirstGraph.addEdge(0,2)
deepFirstGraph.addEdge(0,3)
deepFirstGraph.addEdge(2,4)
deepFirstGraph.addEdge(2,3)
deepFirstGraph.addEdge(3,1)
deepFirstGraph.addEdge(3,5)
deepFirstGraph.deepFirstSearch(0)
/*
广度优先搜索:
从第一个顶点开始,尝试访问尽可能靠近它的顶点。
这种搜索在图上是逐层移动的,首先检查最靠近第一个顶点的层,再逐渐向下移动到离起始顶点最远的层。
广度优先搜索使用抽象的队列而不是数组对已访问过的顶点进行排序:
- 1. 查找与当前顶点相邻的未访问顶点,将其添加到已访问顶点列表及队列中
- 2. 从图中取出下一个顶点v,添加到已访问的顶点列表
- 3. 将所有与v相邻的未访问顶点添加到队列
*/
/**
* @Author: zhuxiankang
* @Date: 2018-10-17 20:29:24
* @Desc: 广度优先搜索
* @Parm:
*/
function BreadthFirstGraph(v) {
// 顶点数
this.vertices = v
// 边数
this.edges = 0
this.adj = []
// 是否被访问
this.marked = []
for(var i=0; i<this.vertices; i++) {
this.adj[i] = []
this.marked[i] = false
}
}
BreadthFirstGraph.prototype.addEdge = function(v, w) {
this.adj[v].push(w)
this.adj[w].push(v)
this.edges ++
}
BreadthFirstGraph.prototype.breadthFirstSearch = function(s) {
// 相邻未访问顶点队列(默认第一个顶点相邻顶点就是自己)
var queue = []
this.marked[s] = true
queue.push(s)
while(queue.length) {
// 从图中取出下一个相邻的顶点
var v = queue.shift()
if(v !== undefined) {
console.log('breadth visited: ', v)
}
// 查找与当前顶点相邻的未访问顶点,将其添加到已访问顶点列表及队列中
for(let w of this.adj[v]) {
if(!this.marked[w]) {
this.marked[w] = true
queue.push(w)
}
}
}
}
let breadthFirstGraph = new BreadthFirstGraph(5)
breadthFirstGraph.addEdge(0, 1)
breadthFirstGraph.addEdge(0, 2)
breadthFirstGraph.addEdge(1, 3)
breadthFirstGraph.addEdge(2, 4)
breadthFirstGraph.breadthFirstSearch(0)
/*
查找最短路径
广度优先搜索对应的最短路径
*/
/**
* @Author: zhuxiankang
* @Date: 2018-10-17 21:13:00
* @Desc: 广度优先搜索对应的最短路径
* @Parm:
*/
function MinPathGraph(v) {
// 从一个顶点到下一个顶点的边
this.edgeTo = []
// 顶点数
this.vertices = v
// 边数
this.edges = 0
this.adj = []
// 是否被访问
this.marked = []
for(var i=0; i<this.vertices; i++) {
this.adj[i] = []
this.marked[i] = false
}
}
MinPathGraph.prototype.addEdge = function(v, w) {
this.adj[v].push(w)
this.adj[w].push(v)
this.edges ++
}
MinPathGraph.prototype.breadthFirstSearch = function(s) {
var queue = []
this.marked[s] = true
queue.push(s)
while(queue.length) {
var v = queue.shift()
if(v !== undefined) {
console.log('min path visited: ', v)
}
for(let w of this.adj[v]) {
if(!this.marked[w]) {
this.marked[w] = true
this.edgeTo[w]= v
queue.push(w)
}
}
}
}
let minPathGraph = new MinPathGraph(5)
minPathGraph.addEdge(1, 2)
minPathGraph.addEdge(1, 4)
minPathGraph.addEdge(2, 4)
minPathGraph.addEdge(0, 1)
minPathGraph.addEdge(0, 2)
minPathGraph.addEdge(1, 3)
minPathGraph.addEdge(2, 3)
minPathGraph.breadthFirstSearch(0)
console.log(minPathGraph.edgeTo)
/**
* @Author: zhuxiankang
* @Date: 2018-10-18 08:50:23
* @Desc: pathTo
* @Parm:
*/
MinPathGraph.prototype.pathTo = function(v) {
// 起始顶点是0
let source = 0
if(!this.marked[v]) {
return undefined
}
let path = []
for(var i = v; i!= source; i=this.edgeTo[i]) {
path.push(i)
}
path.push(source)
return path
}
let minPath = minPathGraph.pathTo(4)
console.log(minPath)
/**
问题记录:
minPathGraph.addEdge(1, 2)
minPathGraph.addEdge(1, 4)
minPathGraph.addEdge(2, 4)
minPathGraph.addEdge(0, 1)
minPathGraph.addEdge(0, 2)
minPathGraph.addEdge(1, 3)
minPathGraph.addEdge(2, 3)
采用这种最小路径法,必须将最小的值放在前面,例如如果这里将
minPathGraph.addEdge(0, 1)
minPathGraph.addEdge(0, 2)
调换位置,变成
minPathGraph.addEdge(0, 2)
minPathGraph.addEdge(0, 1)
那么算出来的最小路径就不对了,例如算0到3的最小路径,算出来是0 - 2 - 3
*/