最短路:朴素Dijkstra和堆优化版Dijkstra

朴素Dijkstra和堆优化版Dijkstra

总体思路

  • 将所有的顶点分为两个部分已知最短距离的顶点集合P未知最短距离的顶点集合Q。最开始的时候,已知最短距离的顶点集合中只有源点。
  • 初始化distance数组,将源点s到自己的距离设置为0,到其他顶点的距离姑且设置为Infinity。
  • 依据distance数组,在未知顶点集合Q中选出距离源点最近的一个顶点u,放入P中,并考察所有以u为起点的边,以u作为中转点,检验是否能够减短源点到其他点的距离。如果有,就更新distance数组。这一步又叫松弛(relaxation)操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzrWFXRR-1591143789255)(../../../图库/1590243662585.png)]

朴素Dijkstra

  • 时间复杂是 O(n^2+m), n 表示点数,m 表示边数
  • 每一次外层循环都找到一个最小路径。
    • 那么N个点,只需要N-1循环

思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBXfEZYp-1591143789257)(../../../../../../%E5%9D%9A%E6%9E%9C%E5%90%8C%E6%AD%A5%E6%96%87%E4%BB%B6%E5%A4%B9/%E6%88%91%E7%9A%84%E5%9D%9A%E6%9E%9C%E4%BA%91/%E7%AE%97%E6%B3%95/%E5%9B%BE%E5%BA%93/1590242798361.png)]

模板

int g[N][N];
int dist[N];
bool st[N];

int dijkstra()
{
    memset(g, 0x3f, sizeof g);//g初始化最大
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n-1; i++)//N个点,只需要N-1循环
    {
        int t = -1;
        for (int j = 1; i <= n; j++)
            if (!st[j] && (t == -1 || dist[j] < dist[t])) // 没被访问过,且找到离源点最小的点的序号。
                t = j;

        st[t] = true;

        for (int j = 1; j <= n; j++)
            dist[j] = min(dist[j], dist[t] + g[t][j]);
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

堆优化Dijkstra

思路

  • 堆维护所有点到起点的距离。
  • 利用堆来代替(且找到离源点最小的点的序号)这一步。
  • 时间复杂度是 O(mlogn)
  • 我们可以将所有修改过的点直接插入堆中,堆中会有重复元素,但堆中元素总数不会大于 m。
typedef pair<int, int> PII;

int n;      // 点的数量
int h[N], w[N], e[N], ne[N], idx;       // 邻接表存储所有边
int dist[N];        // 存储所有点到1号点的距离
bool st[N];     // 存储每个点的最短距离是否已确定

// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    while (heap.size())//会有重复的点,所以需要让heap中数都走一遍
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});// 加入小的边,这里可能加入了重复的点
                // 例如 1->2 = 1 ; 2->1 = 1。
                // 从1点开始,选择1->2
                // 然后会选择 2->1 但是 1已经被加入到最短距离的集合中了
                //所以需要遍历完heap所有元素
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值