吐槽
Dijkstra 翻译为”迪杰特斯拉”,但是音标是/ˈdaɪkstrə/,应该是”戴克特斯拉”才对呀。不过/ai/ /i/只是嘴巴大小不同,可能是传过来时被别的语言音译了。
Go 中没有像 C++ STL 那么多的常用容器,不过 list 可以作为 stack、queue 来用,Go 提供的 heap 需要自己实现一些接口从而实现 priority_queue 的功能。希望 Go 能支持泛型,提供更多容器。
算法特性
Dijkstra
用于求出图中某节点到其他每个节点的最短路径,主要利用 PriorityQueue 数据结构。时间复杂度 O((E+N)lgN),空间复杂度 O(E),E 是边的数量、N 是节点数量。
算法流程
- 1 创建一个数组,存储起始节点到所有目标节点的距离,默认存储无穷大。
- 2 创建一个优先级队列(距离越小越靠前),存储到达某个节点时的(出发点到这点的距离,当前节点 ID),默认放入出发节点(距离 0)。
- 3 弹出一个距离最小的节点,判断是否需要更新距离数组。将所有子节点插入优先级队列同时插入距离累加值。然后在图中删除当前节点为出发点的边。
- 4 循环 3,直到所有队列元素处理完毕。可以提前判断是否所有元素已经处理过,因为距离数组第一次被赋值总是最短的距离。
- 5 遍历距离数组,如果还有无穷大内容,说明有节点无法达到;如果都能达到,则可以记录最大值返回,代表并行出发时的最短距离(或时间)。
算法的应用示例
1 |
|
LeetCode 题目
“818. Race Car”
func racecar(target int) int {
K := 32 - bits.LeadingZeros32(uint32(target))
MAX_RANGE := (1<<K)-1 // 边界距离
dist := make(map[int]int) // 每个位置的最小步数
h := new(myheap)
dist[0] = 0
heap.Push(h, [2]int{0, 0})
for h.Len() > 0 {
tmp := heap.Pop(h).([2]int)
steps1, tar1 := tmp[0], tmp[1]
if val, exists := dist[tar1]; exists && val < steps1 {
continue
}
for k := 0; k <= K; k++ {
tar2 := (1<<k)-1-tar1 // 在这里调头了
steps2 := steps1 + k + 1
if tar2 == target {
steps2-- // 去掉最后的R
}
if val, exists := dist[tar2]; abs(tar2)<=MAX_RANGE && (exists && val>steps2 || !exists) {
heap.Push(h, [2]int{steps2, tar2})
dist[tar2] = steps2
}
}
}
return dist[target]
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
type myheap [][2]int
func (p myheap) Len() int {
return len(p)
}
func (p myheap) Less(a, b int) bool {
return p[a][0] < p[b][0]
}
func (p myheap) Swap(a, b int) {
p[a], p[b] = p[b], p[a]
}
func (p *myheap) Push(val interface{}) {
*p = append(*p, val.([2]int))
}
func (p *myheap) Pop() interface{} {
len := p.Len()
tmp := (*p)[len-1]
*p = (*p)[:len-1]
return tmp
}