算法 - 最短路径(二)- Dijkstra


Dijkstra算法是典型的 单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。

算法思路

Dijkstra算法是一种类似于贪心的算法,步骤如下:

  1. 首先指定一个节点,比如说V0,要计算V0到其他节点的最短路径。

  2. 设置两个集合S和U:

    S中存放已找到的最短路径的顶点,初始时,集合S中只有一个顶点,即V0

    U中存放当前还未找到最短路径的点,初始时,集合U中存放除了V0以外的所有点。

  3. 在U中选取一个距离V0最近的点,并认为那个点到V0的距离为最短路径,将选中的点(设为V1)放入集合S中。

  4. 把V1当做中间节点,更新集合U中的各顶点的距离(遍历V1的出点):if ( 'V1 到 Vn 的距离' + 'V1 到 V0 距离' < 'V0 到 Vn 的距离' ),则更新U。

  5. 重复第3、第4步骤,直到所有节点都包含在S中,则遍历结束。

此处有一张帮助理解的动态图:

在这里插入图片描述

算法分步图解

若存在一个有权图如下图所示:

在这里插入图片描述

  1. 首先我们选定节点A作为原点,并初始化S和U两个集合:

在这里插入图片描述

  1. 执行上述第3、4步骤:

    1. 在U中选取一个距离1号节点最近的点:3号节点,并认为3号节点到1号节点的距离为最短路径,将3号节点点放入集合S中。
    2. 把3号节点当做中间节点,更新集合U中的各顶点的距离(遍历3号节点的出点),并更新U。

在这里插入图片描述

  1. 接着继续在U中选择距离1号节点最近的节点:2号节点。将2号节点放入S集合中,并更新U集合中的值。由于此时以2号节点为中转点并不能减少U中其他节点到1号节点的距离,所以U中的值不会改变。

    在这里插入图片描述

  2. 继续重复上述步骤:将4号节点选入S中。

    在这里插入图片描述

  3. 执行到最后一步是,由于U中只有5号节点,所以5号节点理所当然被选进S集合,此时算法结束:

在这里插入图片描述

算法实现

dist[i]表示当前找到的从源点v0出发到终点vi的最短路径的长度,初始化时,dist[i] = edge[v0][i]

S[i]为0表示顶点vi是否被加入到集合S中,初始化时S[v0]=1,其余为0

path[i]表示v0到vi的最短路径上顶点vi的前一个顶点序号。采用“倒向追踪”方法,确定v0到vi的最短路径上的每个顶点

初始化:dist[k] = edge[v0][k]v0是源点S[v0]=1

递推:
  u = min{dist[t]}s[vt] = 0;

u表示当前T集合中dist数组元素值最小的顶点的序号,以后u加入集合S。

dist[k] = min(dist[k], dist[u] + edge[u][k])S[vk] = 0;

代码实现

void Dijkstra(Vertex s)
{
    while(1){
        V = 未收录顶点中dist最小者;
        if(不存在这样的V)
            break;
        collected[V] = true;			//true表示收录进S集合
        for (V的每个邻接点W)
            if(collected[W] == false)
                if(dist[V]+E<v,w> < dist[W]){	//更新U集合
                    dist[W] = dist[V] + E<v,w>;
                    path[W] = V;				//记录路径
                }
    }
}

算法分析

Dijkstra有一个不足之处,那就是此算法不能计算负权边,要解决负权边,需要用到bellman-ford算法。

在这里插入图片描述

如上图,如果用Dijkstra算法的话就会出错:从1号节点开始,第一步dist[2] = 7, dist[3] = 5;在其中找出最小的边是dist[3] = 5;然后更新dist[2] = 0,最终得到dist[2] = 0,dist[3] = 5,而实际上dist[3] = 2;所以如果图中含有负权值,Dijkstra失效。

这个算法的复杂度是O(n2),空间复杂度也是n2,但通过优先队列优化,可将复杂度将至O(nlog(n))。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值