最短路三种算法详解

短路,三种,算法,详解 · 浏览次数 : 10

小编点评

**算法介绍:** * **Dijkstra 算法**是一种启发式算法,用于求解单源多目标最短路径问题。 * **Spfa** 是一种优先级队列实现的启发式算法,它以优先队列存储可扩展节点,并按优先级排序节点。 * **Floyd** 是一种迭代算法,它使用双向链表存储可扩展节点,并以双向链表更新节点之间的距离。 **代码:** ```python #include #include #include using namespace std; int n, m; int g[N][N]; int dist[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++; } int dijkstra() { memset(dist, 0x3f, sizeof dist); dist[1] = 0; priority_queue, greater> q; q.push({0, 1}); while (q.size()) { auto t = q.top(); q.pop(); int ver = t.second; if (st[ver]) continue; st[ver] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[ver] + w[i]) { dist[j] = dist[ver] + w[i]; q.push({dist[j], j}); } } } if (dist[n] == 0x3f3f3f3f) return -1; return dist[n]; } int main() { memset(h, -1, sizeof h); cin > n > m; while (m--) { int a, b, c; cin > add(a, b, c); } cout << dijkstra() << '\'; return 0; } ``` **优化:** * 使用优先级队列存储可扩展节点,以减少队列中节点的数目。 * 使用双向链表存储可扩展节点,以实现高效的节点访问。

正文

最短路

最短路问题即,给你一张图,让你求出图中两点的最短距离。

这篇文章会讲解 \(Dijkstra\)\(Spfa\)\(Floyd\) 三种算法,让您透彻理解最短路!

Dijkstra

朴素版

题目:

image

\(Dijkstra\) 通常是用来解决图中一个定点到其余点的最短距离,基本思路是:从中心向外扩展,直到扩展到终点为止。

我们用 \(dist[u]\) 来表示从源点 \(s\)\(u\) 的最短距离。

还需要维护一个状态数组 \(st\),用于在更新最短距离时,判断当前的点的最短距离是否需要更新(是否已经确定)。

在每一次扩展的过程中,我们要先找到当前未确定的点的集合中找到一个距离最小的点,也就是:

int t = -1; // 距离最短的点的编号
for (int j = 1; j <= n; ++j) 
    if (!st[j] && (t == -1 || dist[j] < dist[t])) // 判断是否符合条件
        t = j;

那么我们就已经确定这个点了,把它的状态更新一下:

st[t] = true;

然后用这个点更新所有未确定点的最短距离

for (int j = 1; j <= n; ++j) 
    if (!st[i]) // 这句话其实可以不用加,因为我们dijkstra算法的性质已经确定后面确定的点不会再比现在的小了
        dist[j] = min(dist[j], dist[t] + g[t][j]);

完整代码:

#include <iostream>
#include <cstring>
#define N 510
using namespace std;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0; // 源点到源点的距离是0
    for (int i = 1; i <= n; ++i) {
        int t = -1;
        for (int j = 1; j <= 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];
}
int main() {
    memset(g, 0x3f, sizeof g);
    cin >> n >> m;
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c); // 防止重边的情况
    }
    cout << dijkstra() << '\n';
    return 0;
}

此代码时间复杂度为 \(O(n^2)\)

优化

我们发现,朴素 \(Dijkstra\) 主要就耗时在找 \(t\) 上了;在一组数中找到一个最小值,我们自然可以想到用小根堆(不推荐手写)。

于是我们的代码就可以优化成这样了:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define N 200000
typedef pair<int, int> PII; // 存储距离和节点编号
int n, m;
int h[N], e[N], w[N], ne[N], idx; // 因为是点数较多,所以用邻接表存图
int dist[N];
bool st[N];
void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; }
int dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, 1});
    while (q.size()) {
        auto t = q.top();
        q.pop();
        int ver = t.second;
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = h[ver]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i]) {
                dist[j] = dist[ver] + w[i];
                q.push({dist[j], j});
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main() {
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    cout << dijkstra() << '\n';
    return 0;
}

剩下的咕咕,明天再写。

与最短路三种算法详解相似的内容:

最短路三种算法详解

# 最短路 最短路问题即,给你一张图,让你求出图中两点的最短距离。 这篇文章会讲解 $Dijkstra$、$Spfa$、$Floyd$ 三种算法,让您透彻理解最短路! ## Dijkstra ### 朴素版 题目: ![image](https://img2023.cnblogs.com/blog/

最短路径问题

最短路径问题 最短路问题是图论中一种重要的算法,本章将包括: 目录最短路径问题一.概念1.概念2.解决方案二. \(Flord\) 算法1.算法思想2.代码详解3.算法应用及局限性二. \(Djikstra\) 算法1.算法思想2.代码详解3.算法特征及其局限性三. \(Bellman-ford\)

Johnson 全源最短路

Johnson 全源最短路 Johnson 和 Floyd 一样是能求出无负环图上任意两点间最短路径的算法。 引入 求任意两点间的最短路可以通过枚举起点,跑 \(n\) 次 SPFA 来解决,时间复杂度是 \(O(n^2 m)\) 的,也可以用 Floyd 解决,复杂度为 \(O(n^3)\)。 或

LeetCode 周赛 340,质数 / 前缀和 / 极大化最小值 / 最短路 / 平衡二叉树

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 上周跟大家讲到小彭文章风格的问题,和一些朋友聊过以后,至少在算法题解方面确定了小彭的风格。虽然竞赛算法题的文章受众非常小,但却有很多像我一样的初学者,他们有兴趣参加但容易被题目难度和大神选

LeetCode 周赛 346(2023/05/21)仅 68 人 AK 的最短路问题

> **本文已收录到 [AndroidFamily](https://github.com/pengxurui/AndroidFamily),技术和职场问题,请关注公众号 [彭旭锐] 提问。** - [LeetCode 单周赛第 345 场 · 体验一题多解的算法之美](https://mp.wei

P3350 [ZJOI2016] 旅行者

咕了2天才写的题解 还是比较经典的题目,分治处理网格图最短路 离线下来,利用分治的思想,用一条线把网格图平均劈成两半,每次只考虑询问在两块的一对点,所有的线必须经过直线上的一个点,于是我把线上所有点都在规定范围内跑一次dijkstra,最后直接算答案,显然我想让最短路跑的次数最小,每次选较短的边作为

LeetCode 周赛 347(2023/05/28)二维空间上的 LIS 最长递增子序列问题

> **本文已收录到 [AndroidFamily](https://github.com/pengxurui/AndroidFamily),技术和职场问题,请关注公众号 [彭旭锐] 提问。** - 往期回顾:[LeetCode 单周赛第 346 场 · 仅 68 人 AK 的最短路问题](http

使用队列解决迷宫问题(广度优先搜索 / 最短路径)

博客地址:https://www.cnblogs.com/zylyehuo/ # -*- coding: utf-8 -*- from collections import deque maze = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 0,

学习笔记——斯坦纳树

斯坦纳树 斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。 百度百科 在图论里,一般用于解决形如: 给定一个连通图 \(G\),给定 \(k\) 个关键点,选取

Solution -「LOJ #3310」丁香之路

首先有两个前置技巧:1) 两点间的最短距离就是直接连接两点的边的长度;2) 遍历一个子图的最小花费是最小生成树的边权之和乘二。原问题让我们找出一条最短且必经过钦定边的 \(( s, i )\) 路径,那么我们先将 \(\lang s , i \rang\) 连上,问题就变成了找出一条最短且必经过钦定