6、dijkstra算法

 

一、课程目标

  1. 单源最短路径
  2. dijkstra算法
  3. 算法设计
  4. 模拟过程
  5. 优化算法

二、目标详解

1、单源最短路径

给定一个图,给定起点,求该起点到其它点的最短距离,称之为单源最短路径问题。

2、dijkstra算法

dijkstra算法是一个求单源最短路问题的经典算法,应用前提是图中没有负权边。

理解要点一--三个集合:

  • O集合:还没有被松弛连通的顶点。方案:d[x]==oo
  • U集合:已计算出距离,但尚未确定最短路径的顶点。方案:!vis[x] 且 d[x]<oo
  • S集合:已确定最短距离的顶点(和最短距离),显然,如果S包含了所有的顶点,问题也就解决了。方案:顶点用vis[N]记录,最短距离用dis[N]记录。

理解要点二--三个过程:

  • 求最小值:对U集合,求最小值mn和对应顶点x
  • 加入S集合:将该最小距离的顶点x加入到S集合
  • 更新U集合:用x顶点,对其邻接边进行”松弛”,更新距离(d[i])。这是一个更新(及新增)U集合的过程

总结:每次从U集合中选取最短距离的顶点,加入到S集合,然后用该顶点进行松弛操作,更新U集合。显然,这是一个将顶点从O->U->的过程,不断采用贪心策略执行这个迁移过程,直到所有能连通的顶点都加入到了S集合。

3、算法设计

数据设计

  • vis[N]:记录顶点是否在S集合,初始都为false
  • d[N]:起点到某顶点的最短距离,初始d[s]=0,其它为inf,d[x]<inf表示已松弛过
  • g[N][N](或g[N]):邻接矩阵、或邻接表

伪代码(假设s为起点):

初始化(vis[i]都为false、dis[i]都为oo,dis[s]为0);

for(n次) 
  对所有U集合的边求最小值(第一次只有d[s],其它都是oo)
  将最小边加入S集合(vis[min] = true)
  用min顶点进行松弛

4、模拟过程

对上图做dijkstra算法,计算从A开始的所有最短路径。

初始:S集合为空(vis[i]都为false),U集合只有A(d[A] = 0,其它为oo)

以下v为1(红色)表示S集合,d里面红色的表示U集合

-ABCDEF
v000000
d0oooooooooo

第一步:对U求最小(d[A]),加入S,松弛(d[C]=10, d[E]=30, d[F]=100)

-ABCDEF
v100000
d0oo10oo30100

第二步:对U求最小(d[C]),加入S,松弛(d[D]=60)

-ABCDEF
v101000
d0oo106030100

第三步:对U求最小(d[E]),加入S,松弛(d[D]=50,d[F]=90)

-ABCDEF
v101010
d0oo10503090

第四步:对U求最小(d[D]),加入S,松弛(d[F]=60)

-ABCDEF
v101110
d0oo10503060

第五步:对U求最小(d[F]),加入S,松弛(没有邻接边)

-ABCDEF
v101111
d0oo10503060

第六步:对U求最小,没有元素了,返回

5、优化算法

priority_queue(优先队列):优先级高的先出队。每次新入队的元素,会自动被排序到合适的位置,时间复杂度为O(logn)。

在dijkstra算法中,每个顶点都要求U集合中的最小距离,而这里除了最近松弛的边之外,其它边上一次求最小距离时都已经计算过一次,属于重复计算。

使用优化队列,每次将最小值出队,即可解决这个重复计算的问题。

由于优化队列的时间复杂度为O(logn),因此总复杂度为O(nlogn)。

理解:用队列来表示U集合,出队即为找U集合的最短距离,将之加入到S集合,并松弛更新U集合。

三、扩展理解-priority_queue

1、pair方式

priority_queue默认为最大值出队,在dijkstra算法里要最小值出队。

一种方法是要greater<pair<int, int> >,但是整体定义长,容易写错:

priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > q;

//也可以先#typedef简化,例如

typedef pair<int, int> Edge;
priority_queue<Edge, vector<Edge>, greater<Edge> > q;

2、自定义结构体

第二种方法是自定义结构体,重载<运算符

struct Edge {
  int y, w;
  bool operator > (const Edge &e) const {
    return w > e.w;
  }
};

然后定义priority_queue:

priority_queue<Edge, vector<Edge>, greater<Edge> > q;

3、反边法

不管用pair还是用自定义结构体,都可以用反边的方法来简化定义。

例如用pair时,可以在q.push时将边权负一下,这样最大值出队就变成了最小值出队。

或者自定义结构体,重载<运算符,但代码用return w > e.w, 这样也是改变成了最小值出队。

这两种方法都可以简化priority_queue的定义

priority_queue<Edge> q;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值