附相关文章的地址:
- 数据结构之图简介
- 数据结构之图的创建
- 数据结构之图的遍历
- 数据结构之图的广度优先搜索
上一篇文章中,我们实现了基本的广度优先搜索,也就是BFS的工作原理。而广度优先搜索可以做很多事情,接下来,我们来了解如何使用BFS寻找最短路径。
使用BFS寻找最短路径
给定一个图G和源顶点v,找出每个顶点 u 和 v 之间最短路径的距离。
- 我们以顶点之间边的数量来统计其距离,也就是说两个顶点之间的边的数量越小,代表这两个顶点之间的路径就最短。那么
求最短路径也就转换成了求两个顶点之间的边的数量的最小值
。 - 从上一篇的广度优先搜索中,我们知道了,广度优先搜索是访问到某个顶点时,会探索它的邻接顶点列表。也就是说,对于给定顶点v,广度优先搜索会先访问其所有的邻接顶点(也就是所有与其距离为1的顶点),接着去访问距离为2的顶点,以此类推。
实现
- 基于上一篇文章中已经实现广度优先搜索的bfs方法:
this.bfs = function (v) {
let color = initializeColor();
let queue = new Queue();
// 创建 d对象,用来存储从顶点v到顶点u的距离d[u]
let d = {};
// 创建pred对象,用来存储搜索过程中 顶点v的前一个顶点。即:前溯点pred[u],用来推导出从v到其他每个顶点u的最短路径
let pred = {};
queue.enqueue(v);
// 同样的,我们遍历图的所有顶点的列表,将每个顶点都其他顶点的距离初始化为0
for (let i = 0; i < vertices.length; i++) {
// 记录每个顶点到其他顶点的距离
d[vertices[i]] = 0;
pred[vertices[i]] = null;
}
// 循环队列
while(!.queue.isEmpty()) {
// 如果队列非空的话,继续执行以下的操作
// 移除队列的第一项 u
let u = queue.dequeue();
// adjList字典中存储的是顶点和它的邻接顶点列表,所以我们拿到顶点u的邻接列表
let neighbors = adjList.get(u);
// 将访问过的顶点u的状态变更为灰色
color[u] = 'gray'
// 接着探索 顶点u的邻接列表
for (let i = 0; i < neighbors.length; i++) {
// 取出每个邻接顶点
let w = neighbors[i]
// 如果该顶点是白色的,则说明未被访问过,那么此时将其状态颜色变为gray,并将其推入队列中,同时改变 w 顶点和 u顶点之间的距离
if (color[w] === 'white') {
color[w] = 'gray'
d[w] = d[u] + 1;
pred[w] = u;
queue.enqueue(w)
}
}
// 当我们将顶点u的所以邻接顶点完全访问完之后,将顶点u的状态变为黑色
color[u] = 'black';
}
// 访问完顶点之后,返回一个对象
return {
distances: d,
predecessors: pred
}
}
- 改好方法之后,我们调用之后得到以下结果:
// 起始顶点为A顶点
const shortestPathA = graph.bfs(myVertices[0]);
console.log(shortestPathA);
{
distances: {A: 0, B: 1, C: 1, D: 1, E: 2, F: 2, G: 2, H: 2 , I: 3},
predecessors: {A: null, B: "A", C: "A", D: "A", E: "B", F: "B", G: "C", H: "D", I: "E"}
}
可以看到我们拿到了 起始顶点为A 到其他顶点的距离的集合,和 每个顶点的前溯顶点的集合。那么我们就可以根据结果得到起始顶点A到其他顶点的最短路径。看下面代码:
let fromVertex = myVertices[0]; // 即顶点A
// 遍历图的顶点列表
for (let i = 1; i < myVertices.length; i++) {
// 记录下一个顶点
let toVertex = myVertices[i];
// 声明path 栈 用来记录路径
let path = new Stack();
// 追溯 toVertex 到 fromVertex的路径;变量v赋值为其前溯顶点的值,从而进行反向追溯路径
for (let v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) {
// 将顶点v 推入栈中
path.push(v)
}
// 最终将源顶点也推入栈中,得到完整的路径
path.push(fromVertex);
// 栈的规则:先进后出,后进先出,所有我们创建变量s,将栈末尾的源顶点赋值给它。
let s = path.pop();
// 循环将栈中的顶点取出,拼接到s后面
while(!path.isEmpty()) {
s += ` - ${path.pop()}`
}
console.log(s)
}
最终,我们通过上述代码得到了源顶点(也就是起始地顶点A)到图中其他顶点的最短路径。
写在最后
不太好理解,需要仔细琢磨。。。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!