CS61B 19 最短路:Dijkstra与A*

回顾

 BFS 能找到最短路径,而 DFS 却不能。

在效率上,DFS 对于稀疏的图形来说更糟糕。想象一下一个有 10000 个节点的图,所有节点都很稀疏。我们最终会进行 10000 次递归调用,这对空间很不利。
BFS 对于 "杂乱 "的图来说更糟糕,因为我们的队列会被大量使用。

但是,有些图的边带有权重,这时我们就需要新的方法来寻找最短路。

Dijkstra's

思考两个问题:

1. 图中从某一点到另一点的最短路径。

2. 图中从某一点开始遍历所有点的最短路径。

在无权重时,BFS能很好解决问题1.

问题2

从起点s开始的最短路径树可以以如下方式创建:

1. 对图中的每一个点v,找到从s到v的最短路。

2. 合并你上述找到的所有边。

注意到最短路径树永远是一棵树。有v个结点的树拥有v-1条边。除根节点外,对于每个节点,在edgeTo array上都有一个父节点。

Dijkstra概述:

它输入一个节点s,输出从s开始的最短路径树。

1. 创建一个优先队列priority queue

2. 将s以优先级0加入到优先队列中。将所有其他节点以优先级∞加入到优先队列中。

3. 当优先队列不是空的:将一个节点弹出优先队列,然后释放(relax)所有以该点为起点的有向边。

释放是什么意思?

假设我们从优先队列里去除的顶点是v,我们看v的所有边,比如说(v,w),我们试着释放这条边:

看当前从根节点source到w的最佳距离curBestDistToW 和 curBestDistToV + weight(v,w),称为potentialDistToWUsingV.

potentialDistToWUsingV是否更好,或者说是否小于curBestDistToW?如果是,将curBestDistToW设置成=potentialDistToWUsingV,并且将edgeTo[w]更新为v。(edgeTo应该指向遍历顺序中的上一个节点

注意:从不释放已经访问过的节点的边。

伪代码:

def dijkstras(source):
    PQ.add(source, 0)
    For all other vertices, v, PQ.add(v, infinity)
    while PQ is not empty:
        p = PQ.removeSmallest()
        relax(all edges from p)
def relax(edge p,q):
   if q is visited (i.e., q is not in PQ):
       return

   if distTo[p] + weight(edge) < distTo[q]:
       distTo[q] = distTo[p] + w
       edgeTo[q] = p
       PQ.changePriority(q, distTo[q])

只要边都是非负的,Dijkstra 的方法就能保证最优。

图解

例:

带权图。

为了防止循环,我们需要数组记录.

其中distTo为该点距开始节点的最短距离,edgeTo为在目前的最小生成树中指向该节点的上一个节点。

首先,我们把所有可供选择的节点加入Fringe队列(最小优先队列)中,括号里第一个元素是节点标号,第二个是距开始节点的最短距离(一开始都为无穷)。

对从0出发的两个边分别做relax。1、2两节点的edgeTo更新(为上一节点即0)。2<∞,1<∞,故将2、1两节点距离distTo更新,优先级fringe更新(数值与distTo相等)。

然后,2的优先级(为1)最低,将2弹出(pop)优先队列fringe

在从2继续,释放(relax)从2起始的边(2,5)

1的优先级最低,弹出(pop)节点1

relax三条边。最下面:5>1,不需更新。其他节点需要更新。

4的优先级最低,pop4。relax三条边,(4,2) 5+1>1,不需要替换。其他两个<  替换。

5的优先级最低,pop5

...

注意:
 总是按照与源点的总距离顺序排列顶点。
 在访问过(白色)顶点的边上,relaxation 总是失败。

负数边

如果边的权重出现负数,Dijkstra就不能总是找到最佳路径。这时可以试用belmanFord等方法。

A*

一个起点的前提下,如果我们只有一个目标,在起点和终点相距非常远的大图下Dijkstra's 会非常非常慢,因为Dijkstra's是以BFS为基础的,需要很长时间才能挖到足够深的地方。

在Dijkstra的基础上,我们在计算优先级的时候加一个数据,这个数据估算各个节点到终点的大致距离。

这组数据 h(v,goal) 称为启发式(Heuristics),它的生成可以使用各种方法(例如具体图片上两点之间直线距离,这里省略),但注意它只是估算,所以不必过于复杂精确。加上权重过后,A*树能很高效地找到两个确定点之间的最短路。

A*的特点:

1. A*并不访问所有节点。

2. 结果不再是有效的最短路径树。

启发式生成的好坏直接决定A*算法的效率。总而言之,好的启发式需要以下两点:

1. Admissibility(不知道咋翻译):h(v,target)要小于等于v到target的真实距离。

2. Consistency一致性:对于w的每个相邻节点v:

        1. h(v, target)<=distance(v, w)+h(w, target);

        2. 其中,dist(v, w) 是 v 到 w 的边的权重。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值