图的最短路径
最短路径分为两种:
(1)单源路径:从某顶点出发,到其他全部顶点的最短路径
(2)顶点间的最短路径:任意两个顶点之间的最短路径
最短路径的结果主要有两个方面:
(1)顶点之间最短路径的长度
(2)从源顶点到目标顶点的路径
BFS
只有边权为 1 时才能用BFS求最短路。
为什么BFS能求边权为1的图的最短路呢?
其实可以把整个遍历路径看成一棵树
我们会看到是一层一层遍历的
也就是说 只要这个节点被访问到了,那么根节点到这个节点的最短路径就已经确定了
可以看到 2到1的最短路径就是1到6也是1
以此类推,2到8的最短路径就是3
从2出发,计算2到8的最短路径
第一步:初始化
d[i]=∞
path[i]=-1
d[2]=0 //2到自身的路径长度为0
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | inf | 0 | inf | inf | inf | inf | inf | inf |
path[] | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | F | T | F | F | F | F | F | F |
queue | 2 |
---|
第二步:从2出发,找到邻接点1、6
2出队 1、6进队
queue | 1 | 6 |
---|
path存的是当前节点的父节点,所以path[1] = 2 path[6] = 2
d[1] = d[2]+1 = 1
d[6] = d[2]+1 = 1
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | 1 | 0 | inf | inf | inf | 1 | inf | inf |
path[] | 2 | -1 | -1 | -1 | -1 | 2 | -1 | -1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | T | T | F | F | F | T | F | F |
第三步:节点1出队,找到邻接节点5
1出队 5进队
queue | 6 | 5 |
---|
d[5] = d[1]+1 = 2
path[5] = 1
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | 1 | 0 | inf | inf | 2 | 1 | inf | inf |
path[] | 2 | -1 | -1 | -1 | 1 | 2 | -1 | -1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | T | T | F | F | T | T | F | F |
第四步:节点6出队,找到邻接节点2、3、7
6出队 3、7进队,2访问过,忽略
queue | 5 | 3 | 7 |
---|
path[3] = 6
path[7] = 6
d[3] = d[7] = d[6]+1 = 2
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | 1 | 0 | 2 | inf | 2 | 1 | 2 | inf |
path[] | 2 | -1 | 6 | -1 | 1 | 2 | 6 | -1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | T | T | T | F | T | T | T | F |
第五步:节点5出队,没有邻接节点
5出队
queue | 3 | 7 |
---|
第六步:节点3出队,找到邻接节点6、4、7
3出队,4入队,6、7已经被访问过了 忽略
queue | 7 | 4 |
---|
d[4] = d[3]+1 = 2+1 = 3
path[4] = 3
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | 1 | 0 | 2 | 3 | 2 | 1 | 2 | inf |
path[] | 2 | -1 | 6 | 3 | 1 | 2 | 6 | -1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | T | T | T | T | T | T | T | F |
第七步:节点7出队,找到邻接节点3、4、8
7出队,8入队,3、4已经被访问过了 忽略
queue | 4 | 8 |
---|
d[8] = d[7]+1 = 2+1 = 3
path[8] = 7
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
d[] | 1 | 0 | 2 | 3 | 2 | 1 | 2 | 3 |
path[] | 2 | -1 | 6 | 3 | 1 | 2 | 6 | 7 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
visited | T | T | T | T | T | T | T | T |
第七步:节点4出队,找到邻接节点3、7、8
3、7、8已经被访问过了 忽略
queue | 8 |
---|
第八步:节点8出队,找到邻接节点4、7
4、7、已经被访问过了 忽略
queue |
---|
至此,广搜结束,输出结果d[8] = 2
代码实现
const bfs = (graph,node)=>{
const n = Object.keys(graph).length
const visited = new Array(n+1).fill(false)
const max = Number.MAX_SAFE_INTEGER
const d = new Array(n).fill(max)
const path = new Array(n+1).fill(-1)
d[node] = 0
visited[node] = true
const queqe = [node]
// 开始深搜
while(queqe.length){
const newnode = queqe.shift()
const v = graph[newnode]
v&&v.map(item=>{
// 这个节点没有访问过
if(!visited[item]){
path[item] = newnode
d[item] = d[newnode]+1
visited[item] = true
queqe.push(item)
}
})
}
// 输出路径
const rspath = [8]
while(rspath[0]===node){
rspath.unshift(path[rspath[0]])
}
console.log(rspath) // 2 6 7 8
return d[8] //3
}
const graph = {
1:[2,5],
2:[1,6],
3:[6,7,4],
4:[3,7,8],
5:[1],
6:[2,3,7],
7: