广度优先算法: 解决 对于非加权图, 找出两点之间最短路径
狄克斯特拉算法: 解决 加权图(非负向), 找出两点之间耗时最短的路径
问题: 找出起点——终点的耗时最短的路径
解决: 狄克斯特拉算法
- 第一步: 假设你站在 起点, 你到A点耗时 6分钟, 到B点耗时2分钟, 那么选出耗时最短的节点B, 现在还不确定到终点的时间, 假设为无穷大
- 第二步: 计算B点到附近节点的时间
(此时,可以发现 从起点——A的最短路径 不是 6 而是从起点——B——A 耗时5分钟)
1. 前往节点A的更短路径(时间从6分钟缩短到5分钟)
2. 前往终点的更短路径(时间从无穷大缩短到7分钟)
- 第三步: 重复以上步骤
- 找出 B点到 附近节点耗时最短的节点A
- 计算 A点到 附近节点的时间
(此时,可以发现 从起点——终点的最短路径 不是 7 而是从起点——B——A——终点 耗时6分钟)
代码实现
第一步: 需要三个Hash表,第一个存储整个图graph, 第二个存储耗时costs, 第三个 更新路径parents
const graph: Graph = {
start: { A: 6, B: 2 },
A: { fin: 1 },
B: { A: 3, fin: 5 },
fin: {}
} // 图
const costs: Graph = {} // 起点到各个点的耗时
const parents: Graph = {} // 路径更新
const processed: Array<string> = [] // 保存处理过的节点, 以防出现 环图
Object.keys(graph).forEach(key => {
if (key !== 'start') {
costs[key] = graph.start[key]??Infinity
parents[key] = graph.start[key] ? 'start' : null
}
})
第二步: 从 耗时costs中找出 耗时最短的节点, 并且计算 和 更新 到附近节点的开销
function updateCosts () {
const node: null|string = findLowsetCostNode(costs) // 获取耗时最短的节点
if (!!node) {
const cost = costs[node]
const neigbors = graph[node]
for(const n in neigbors) {
const new_cost = cost + neigbors[n]
if (new_cost < costs[n]) {
costs[n] = new_cost
parents[n] = node
}
}
processed.push(node)
updateCosts()
}
}
updateCosts()
-
- 从起点(假设为O)开始, 得到 耗时最短的节点是B点
-
- 从B点出发, 计算B点到 附近节点(A点和终点)的耗时: 起点到B点耗时 + B点到该点的耗时 = 起点到该点的耗时,类似于向量的计算 : 如果 耗时 小于costs表 则更新 值,同时更新 parents表
- 3. 从 耗时表里 找出耗时 最点的节点 (B点已经被处理过,所以排出在外), 那么得到耗时最短的点是A点
-
- 请A点出发, 计算 A点 到 附近点(这里只有 终点了)的耗时, 和 第2步一样,计算 更新
- 5. 最后得出, 从起点到终点的耗时最短的时间是起点——B点——A点——终点, 共计耗时: 6分钟
完整代码:
const graph: Graph = {
start: { A: 6, B: 2 },
A: { fin: 1 },
B: { A: 3, fin: 5 },
fin: {}
} // 图
const costs: Graph = {} // 起点到各个点的耗时
const parents: Graph = {} // 路径更新
const processed: Array<string> = [] // 保存处理过的节点, 以防出现 环图
Object.keys(graph).forEach(key => {
if (key !== 'start') {
costs[key] = graph.start[key]??Infinity
parents[key] = graph.start[key] ? 'start' : null
}
})
// 找出耗时最点的节点
function findLowsetCostNode (costs: Graph): string|null {
const keys:Array<string> = Object.keys(costs).filter(key => {
return !processed.includes(key)
})
if (!keys.length) return null
let min = 0
for (let i = 1; i < keys.length; i++) {
costs[keys[i]] < costs[keys[min]] && (min = i)
}
return keys[min]
}
// 主程序
function main () {
const node: null|string = findLowsetCostNode(costs)
if (!!node) {
const cost = costs[node]
const neigbors = graph[node]
for(const n in neigbors) {
const new_cost = cost + neigbors[n]
if (new_cost < costs[n]) {
costs[n] = new_cost
parents[n] = node
}
}
processed.push(node)
updateCosts()
}
}
main()
练习:找出起点到终点的耗时最短的路径
const graph: Graph = {
start: {
A: 5,
B: 2,
},
A: {
C: 4,
D: 2
},
B: {
A: 8,
D: 7
},
C: {
D: 6,
fin: 3
},
D: {
fin: 1
},
fin: {}
}
// parents: { A: 'start', B: 'start', C: 'A', D: 'A', fin: 'D' }
// costs: { A: 5, B: 2, C: 9, D: 7, fin: 8 }
答案: 起点 —— A点 —— D点 —— 终点 共计耗时: 8分钟