广度优先搜索可回答两类问题。
第一类问题:从节点A出发,有前往节点B的路径吗?
第二类问题:从节点A出发,前往节点B的哪条路径最短?
今天我们用图来解决这个问题
使用图来创建问题模型。解决最短路径问题的算法被称为广度优先搜索 。
什么是图
图由节点和边组成。一个节点可能与众多节点直接相连,这些直接相连的节点被称为邻居。
无向图
(undirected graph)没有箭头,直接相连的节点互为邻居。
有向图
节点 7,8,10 都没有邻居,因为没有从他们出发指向其他人的箭头。
被称为有向图 (directed graph),其中的关系是单向的。
下面的图更直观,方便理解。
要求:从节点-1 到达 节点-10 的最短路径。
最短路径是 节点-1 经过 节点-2 到达 节点-10。
当然经过节点-6 或节点-5 也能到达节点-10,但不是最短路径。
JS实现版本
我们用对象来表达上图的关系。
每个节点都是一个对象的成员数组,再将节点的邻居(直接相连的节点)依次加入到数组中。
let graph = {};
graph['node-1'] = ['node-2', 'node-3', 'node-4',]
graph['node-2'] = ['node-5', 'node-6', 'node-10']
graph['node-3'] = ['node-5']
graph['node-4'] = ['node-7', 'node-8']
graph['node-5'] = ['node-10']
graph['node-6'] = ['node-10']
// graph['node-7'] = []
// graph['node-8'] = []
const graphSearch = (graph, start = 'node-1', target = 'node-10') => {
// history 记录查询过的节点
let history = []
, searchList = graph[start]
;
if (searchList.includes(target)) {
return true
}
while (searchList.length) {
// 模拟队列弹出一个 node 元素
let node = searchList.splice(0, 1).toString()
// 此 node 的邻居数组
, list = graph[node]
;
// 当前 node 不在查询记录中进行查询
if (!history.includes(node)) {
// 加入查询记录
history.push(node)
if (list) {
if (list.includes(target)) {
console.log(node, graph[node]);
return true
}
// node 不存在于 searchList 才加入查询队列
// for (let i of list){
// if (!searchList.includes(i)) {
// searchList.push(i)
// }
// }
// 数组去重 简洁的写法
searchList = [...new Set(searchList.concat(list))]
}
}
}
return false
}
console.log(graphSearch(graph));