# 图
# 实现无向图
# 基础属性和方法
function Graph() {
// 存储顶点
this.vertices = []
// 存储边
this.adjacentList = {}
}
// 录入顶点
Graph.prototype.addVertex = function (v) {}
// 录入边
Graph.prototype.addEdge = function (first, second) {}
// 打印当前图的结构
Graph.prototype.print = function (first, second) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# addVertex
- 录入当图的顶点
Graph.prototype.addVertex = function (v) {
if(!this.vertices.includes(v)) return new Error('顶点重复')
this.vertices.push(v)
this.adjacentList[v] = []
}
1
2
3
4
5
2
3
4
5
# removeVertex
- 删除顶点别忘了同时也要删除,边中的关系
Graph.prototype.removeVertex = function (v) {
// 删除顶点
if(this.vertices.includes(v)) {
this.vertices.splice(this.vertices.indexOf(v))
}
// 删除相关边
Object.keys(this.adjacentList).forEach(item=>{
if (this.adjacentList[item].includes(v)){
this.adjacentList[item].splice(this.adjacentList[item].indexOf(v))
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# addEdge
Graph.prototype.addEdge = function (first, second) {
// 但录入边信息时,判断顶点在不在,如果不在,自动添加该顶点信息
if (!this.adjacentList[first]) this.addVertex(first)
if (!this.adjacentList[second]) this.addVertex(second)
!this.adjacentList[first].includes(second)
? this.adjacentList[first].push(second) : undefined
!this.adjacentList[second].includes(first)
? this.adjacentList[second].push(first) : undefined
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# removeEdge
- 删除边,要删除两个顶点中的相互依赖数据
/**
* @params source {*} 顶点一
* @params destination {*} 顶点二
*/
Graph.prototype.removeEdge = function (source, destination) {
if (this.adjacentList[source] && this.adjacentList[source].includes(destination)) {
let destinationIndex = this.adjacentList[source].indexOf(destination)
this.adjacentList[source].splice(destinationIndex,1)
let sourceIndex = this.adjacentList[destination].indexOf(source)
this.adjacentList[destination].splice(sourceIndex,1)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Graph.prototype.print = function (first, second) {
let template = ``
this.vertices.forEach(vertex=>{
template += `${vertex} =>`
this.adjacentList[vertex].forEach(edge=>{
template+=` ${edge} `
})
template+='\n'
})
return template
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 打印结果为
a => b c d e f
b => a f e d
c => a
d => a b r
e => a b
f => a b
r => d
1
2
3
4
5
6
7
2
3
4
5
6
7
# 广度优先遍历
- 要实现广度优先,需要一个记录节点有没有被遍历的工具函数
- 我们用这个工具函数先定义所有节点为未探索状态
black
,类似我们玩文明,红色警戒中的未探索地图 gray
代表发现了该节点,还没有去过white
代表已经去过该节点
/**
* white 已探索
* gray 已发现未探索
* black 未发现
*/
Graph.prototype._initColor = function() {
let color = {}
this.vertices.forEach(item=>{
color[item] = 'black'
})
return color
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# BFS
- 需要用到
队列数据结构
来配合完成 去回顾队列 - 详细解释在代码中进行注释
Graph.prototype.BFS = function (v, callback) {
let color = this._initColor() // 初始化,让全部节点变为'black',并返回该对象
let queue = new Queue()
queue.enqueue(v || this.vertices[0])// 将传入的顶点加入队列
while (!queue.isEmpty()) {
// 将当前顶点出队列,由于之前实现的队列返回的是节点(用链表实现的队列)
// Node { value: xx, next: xx }
let currentVertex = queue.dequeue()
// 在这里将节点值解构出来
let { value } = currentVertex
// 那道该节点所对应的边
let currentEdgeList = this.adjacentList[value]
// 举例:类似于我们在当前节点发现了该节点周围的节点
currentEdgeList.forEach(edge => {
if (color[edge] === 'black') {
// 将这些节点状态改为已发现状态,并加入队列
color[edge] === 'gray'
queue.enqueue(edge)
}
});
// 如果第一次到达该节点,执行回调函数中的方法
if(color[value] !== 'white'){
color[value] = 'white'
callback && callback(value)
}
}
}
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
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
# 测试
let gg = new Graph()
gg.addVertex('a')
gg.addVertex('b')
gg.addVertex('c')
gg.addVertex('d')
gg.addVertex('e')
gg.addVertex('f')
gg.addEdge('a','b')
gg.addEdge('a','c')
gg.addEdge('a','c')
gg.addEdge('a','d')
gg.addEdge('a','e')
gg.addEdge('a','f')
gg.addEdge('b','f')
gg.addEdge('b','e')
gg.addEdge('b','d')
gg.addEdge('r','d')
gg.BFS('b',function (value) {
console.log(`------------${value} 已探索--------------`);
})
------------b 已探索--------------
------------a 已探索--------------
------------f 已探索--------------
------------e 已探索--------------
------------d 已探索--------------
------------c 已探索--------------
------------r 已探索--------------
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
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
# 深度优先遍历
Graph.prototype._DFSHandler = function (v,color,callback) {
color[v] = 'gray'
let currentEdge = this.adjacentList[v]
// 遍历找到符合递归条件的顶点
currentEdge.forEach(edge => {
if (color[edge] === 'black') {
this._DFSHandler(edge,color,callback)
}
});
color[v] = 'white'
callback && callback(v)
}
Graph.prototype.DFS = function (v,callBack){
let color = this._initColor()
this._DFSHandler(v, color, callBack)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18