图论:图的四种最短路径算法

article/2025/8/25 5:38:54

目录:

 1.DFS(单源最短路径算法)

例题1:

 DFS题目分析:

代码DFS:

 2.Floyed(时间复杂度On^3)

1.应用场景:

2.解析算法:    

核心代码1:

我的笔记

核心代码2:

 Floyd例题:

3.Dijksyta算法

1.应用场景:

2.算法描述:

1.初始化:

2.for:

核心代码:

3.例题:

 注意:

 代码如下:

4.SPFA算法

1.算法思想:

2.注意:

3.算法分析:

4.核心代码:

5.例题:

 题目分析:

代码如下:

5.总结:

那让我为大家介绍这四种算法吧!

 1.DFS(单源最短路径算法)

例题1:

        建立一个有向图,n代表城市个数,有m行连接数据,x代表连接初始点,y代表连接点,r代表线权。求城市1到城市5的最短路径。

输入:

5 8
1 2 2
2 3 3
3 4 4
4 5 5
5 3 3
1 5 10
3 1 4
2 5 7

输出:

9

 DFS题目分析

        用dfs进行搜索的话,递归的出口是什么?->

        当然是扫描到最后一个城市的时候,然后记录下此时的路径值,如果之后搜索的测试值比之前的值小,则更新路径的值,搜索完所有的路径后,输出最小值,其中用VIS数组进行标记和回溯。

代码DFS:

#include <iostream>
using namespace std;
//从城市1到城市5最短路径为多少?
int mp[105][105];//图
int vis[105];//测试数组
int x, y, r;
int n; int m;
int minx = 1000000;
void dfs(int step, int sum) {if (sum > minx) {return;}if (step == n) {//当扫描到最后一个城市时		if(sum<minx){minx = sum;//更新return;}}for (int i = 1; i <=n; i++) {if (mp[step][i] != 0 &&vis[i]==0) {//该点没有被标记,且该点存在连接vis[i] = 1;dfs(i, sum + mp[step][i]);vis[i] = 0;}}
}
int main()
{cin >> n>>m;while (m--) {cin >> x >> y >> r;mp[x][y] = r;//该图为有向图,是由x到y的距离}dfs(1, 0);cout << minx << endl;
}

 2.Floyed(时间复杂度On^3)

1.应用场景

1.多源最短路径。(缺点:时间复杂度相对较高,但是可以解决负权边问题)

2.找最小环。

3.倍增。

2.解析算法:    

        通过插入点和中转点来缩短路径,先将图中各点连线都初始化为无穷,再进行建图,中转所有的点,不断更新最小值输出:

核心代码1:

 for (int k = 1; k <= n; k++) {//从1到n依次各点进行中转for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (e[i][j] > e[i][k] + e[k][j]) {//如果该路径更短,更新成该路径e[i][j] = e[i][k] + e[k][j];}}}}

我的笔记

然后就是我的笔记啦:(还是比较详细的)

         这里由不得思考一个问题,Floyd算法无非就是动态规划,状态转移方程e[i][j]=max(e[i][j],e[i][k]+e[k][j])

核心代码2:

void floyed(){for (int k = 1; k <= n; k++) {//从1到n依次各点进行中转for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (e[i][j] > e[i][k] + e[k][j]) {//如果该路径更短,更新成该路径e[i][j] = max(e[i][j],e[i][k] + e[k][j]);}}}}
}

 Floyd例题:

AcWing 854 Floyd求最短路

题目描述:

给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。

再给定k个询问,每个询问包含两个整数x和y,表示查询从点x到点y的最短距离,如果路径不存在,则输出“impossible”。

数据保证图中不存在负权回路

输入格式

第一行包含三个整数n,m,k

接下来m行,每行包含三个整数x,y,z,表示点x和点y之间存在一条有向边,边长为z。

接下来k行,每行包含两个整数x,y,表示询问点x到点y的最短距离。

输出格式

共k行,每行输出一个整数,表示询问的结果(最小路径),若询问两点间不存在路径,则输出“impossible”。

数据范围

1≤n≤200,
1≤k≤n^2
1≤m≤20000,
图中涉及边长绝对值均不超过10000。

输入:

3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3

输出:

impossible
1

题目注意:

1.初始图矩阵的建立。

2.如果有重复的边如何处理。

3.输出的时候如何判断x,y没有路径。

代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 1e9;
int n, m, k;
int x, y, r;
int e[300][300];
void floyed() {for (int k = 1; k <= n; k++) {//从1到n依次各点进行中转for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (e[i][j] > e[i][k] + e[k][j]) {//如果该路径更短,更新成该路径e[i][j] = max(e[i][j], e[i][k] + e[k][j]);}}}}
}
int main()
{cin >> n >> m >> k;for (int i = 1; i <= n; i++) {//建立初始的图,赋值for (int j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;}else {e[i][j] = INF;}}}while (m--) {cin >> x >> y >> r;e[x][y] = min(e[x][y], r);//处理重复边的值}floyed();while(k--){cin >> x >> y;if (e[x][y] > INF / 2) {//说明x到y没有路可以走cout << "impossible" << endl;}else {cout << e[x][y] << endl;//输出最短路径}}
}

         今天的分享暂时先到这里,明天持续更新.....

3.Dijksyta算法

1.应用场景:

单源路径最短(我只看出来了这种)时间复杂度(On^2)

注意:不能求负权值.

2.算法描述:

设起点为x,dis[v]表示s到v的最短路径

1.初始化

起点初始化为0。其余点初始化为无穷大

2.for:

a.在没有访问的顶点中找到一个顶点u,使得dis[u]是最小的。(不断搜索到下一个路径最小的点,更新)。

b.u为已确定的最短路径(将不再对该点及之前的点进行搜索)。

核心代码:

int dijkstra(int n, int m) {//n为顶点数,m为起点开始的位置   while (true) {fill(dis, dis + maxn, INF);dis[m] = 0;//初始化起点为0int index = -1;int minx = 0;//定义for (int i = 1; i <= n; i++) {if (!vis[i] && minx > dis[i]) {//寻找到该点index = i;minx = dis[i];}}if (index == -1) {//说明没有点可以继续搜索了break;//退出循环条件}vis[index] = 1;//已经确定该点为最短路径点了,标记上踢出for (int j = 1; j <= n; j++) {if (dis[j] > dis[index] + mp[index][j]&&vis[j]==0&&mp[index][j]!=INF) {//该点有路可以走dis[j] = dis[index] + mp[index][j];//值得思考有DP思想}}}
}

3.例题:

(改题目来源于算法笔记)

题目要求:求V0到其他位置s的最短路径。

输入格式

n为有几个顶点,m为几条边,s为起点。

第二行到第m+1行输入x,y,r,分别为x结点到y结点,边权为r。

输出格式:从s到个顶点的最短路径。

输入:

6 8 0
0 1 1
0 3 4
0 4 4
1 3 2
2 5 1
3 2 2
3 4 3
4 5 3

输出:

0 1 5 3 4 6

 题目分析: 不断去找路径最短的那个顶点,标记,搜索下一个最短顶点即可。(图示->)

 注意:

1.vis数组的标记。

2.更新顶点,没有路径的点就不进行扫描。

3.循环的终止条件。

 代码如下:

#include<iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000;//规定一个最大顶点数
const int INF = 199999999;
int n, m, s;
int mp[maxn][maxn];
int dis[maxn];
bool vis[maxn] = { false };
void Dijkstra(int s) {memset(dis, 0x7f, sizeof(dis));dis[s] = 0;for (int i = 1; i <= n; i++) {//循环了n次int index = -1;int minx = INF;for (int j = 0; j < n; j++) {if (vis[j] == false && dis[j] < minx) {index = j;//记录这个搜索到的路径最小的点。minx = dis[j];//更新最小值}}if (index == -1) {//没有路可以走了return;}vis[index] = true;//标记该点for (int i = 0; i < n; i++) {if (vis[i] == false && mp[index][i] != INF && dis[index] + mp[index][i] < dis[i]) {dis[i] = dis[index] + mp[index][i];//优化更新dis[i]}}}
}
int main() {int x, y, r;cin >> n >> m >> s;memset(mp, 0x7f, sizeof(mp));for (int i = 1; i <= m; i++) {cin >> x >> y >> r;mp[x][y] = r;}Dijkstra(s);//将起点输入进去for (int i = 0; i < n; i++) {cout << dis[i] << " ";}return 0;
}

      完美撒花!继续更新SPFA算法。  

4.SPFA算法

1.算法思想:

队列优化,去掉一些无用的松弛操作,用队列来维护松弛造作的点。继承了Bellman-Ford算法的思想,但时间复杂度相对来说提高了很多。

与BFS的算法有一些类似,利用了STL队列。

2.注意:

虽然大多数情况spfa跑的比较快,但时间复杂度仍为(Onm),主要用应用于有负边权的情况(如果没有负边权,推荐使用Dijkstra算法)。利用了邻接表建图,数据结构的基础一定要掌握好,而且该算法很容易超时,被卡,必须要谨慎选择该算法。

3.算法分析:

1.用dis数组记录点到有向图的任意一点距离,初始化起点距离为0,其余点均为INF,起点入队。

2.判断该点是否存在。(未存在就入队,标记)

3.队首出队,并将该点标记为没有访问过,方便下次入队。

4.遍历以对首为起点的有向边(t,i),如果dis[i]>dis[t]+w(t,i),则更新dis[i]。

5.如果i不在队列中,则入队标记,一直到循环为空。

4.核心代码:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int INF = 1000000000;
const int maxn = 1000;
int dis[maxn];//记录最小路径的数组
bool vis[maxn];//标记
struct node {int s1;//记录结点int side;//边权
};
vector<node>mp[maxn];//用vector建立邻接表
void Spfa(int s) {queue<int>v;vis[s] = 1; v.push(s); dis[s] = 0;while (!v.empty()) {int q = v.front();v.pop(); vis[q] = 0;for (int i = 0; i < mp[q].size(); i++) {if (dis[mp[q][i].s1] > dis[q] + mp[q][i].side) {dis[mp[q][i].s1] = dis[q] + mp[q][i].side;//更新最短路径。            if (!vis[mp[q][i].s1]) {//是在更新新的值条件里面判断,一定特别注意这点v.push(mp[q][i].s1);vis[mp[q][i].s1] = 1;//标记未标记过的点}}}}
}

 完美撒花!!!

5.例题:

P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目背景:

本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过(但本题可以用SPFA过),如有需要请移步 P4779

题目描述:

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出样例:

输入 :

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出 :

0 2 4 3

 样例说明:

图片1到3和1到4的文字位置调换

 题目分析

建立一个有向图,输出s到第i个结点的最短距离。(无疑是套刚刚那个模板

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 10001;
const long long INF = 2147483647;
int dis[maxn];//记录最小路径的数组
int vis[maxn];//标记
int n, m, s;
struct node {int s1;//记录结点int side;//边权
};
void init() {for (int i = 1; i <= n; i++) {dis[i] = INF;vis[i] = 0;}
}
vector<node>mp[maxn];//用vector建立邻接表
void Spfa(int s) {queue<int>v;   vis[s] = 1; v.push(s); dis[s] = 0;while (!v.empty()) {int q = v.front();v.pop(); vis[q] = 0;for (int i = 0; i < mp[q].size(); i++) {if (dis[mp[q][i].s1] > dis[q] + mp[q][i].side) {dis[mp[q][i].s1] = dis[q] + mp[q][i].side;//更新最短路径。if (vis[mp[q][i].s1]) {continue;//如果已经标记,则继续下一次循环}v.push(mp[q][i].s1);}}}
}
int main()
{int x, y, r;cin >> n >> m >> s;init();while (m--) {node h;cin >> x >> y >> r;h.s1 = y;//因为该图为有向图,记录指向的结点h.side = r;//记录路径mp[x].push_back(h);}Spfa(s);for (int i = 1; i <= n; i++) {        cout << dis[i] << " ";}
} 

        于是我->

         AC了,那SPFA算法就到此结束了,总体来说注意细节,在数据较大时候谨慎使用.

5.总结:

1.DFS,Dijkstra,SPFA主要解决单源最短路径

2.Floyed时间复杂度较高,但是可以解决多源最短路径。

3.Dijkstra虽然效率比较高,但是无法解决负权值的问题。

4.SPFA在数据较大的时候容易被卡,但更加有利于解决有负边权的情况,以及判断是否有负环。

5.在图论中一定要掌握好邻接表和邻接矩阵的建立。

         基础知识充分了解之后,就是形成知识网络练习的过程了,希望阅读该文章后能让自己以及读者在图论方面有更深刻得到理解。图,何止是图!!!


http://chatgpt.dhexx.cn/article/rB08FlhM.shtml

相关文章

图的五种最短路径算法

本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,费罗伊德算法,迪杰斯特拉算法,Bellman-Ford 算法。 1)深度或广度优先搜索算法(解决单源最短路径) 从起点开始访问所有深度遍历路径或广度优先路径,则到达终点节点的路径有多条,取其中路径权值最短的一…

最短路径算法——Dijkstra算法——python3实现

本文参考来自数据结构与算法分析 java语言描述。 文章目录 问题描述问题分析实现过程如何使用数据变化表代码实现优先队列中的堆排序使用set代替优先队列得到最短路径 负权边算法改进&#xff08;若为无圈图&#xff09; 问题描述 现有一个有向赋权图。如下图所示&#xff1a;…

最短路径算法的编程与实现 C语言

一 、目的&#xff1a; 1.掌握最短路径算法的基本原理及编程实现&#xff1b; 二 、环境&#xff1a; operating system version&#xff1a;Win11 CPU instruction set: x64 Integrated Development Environment&#xff1a;Viusal Studio 2022 三 、内容&#xff1a; 1…

图的四种最短路径算法

本文总结了图的几种最短路径算法的实现&#xff1a;深度或广度优先搜索算法&#xff0c;弗洛伊德算法&#xff0c;迪杰斯特拉算法&#xff0c;Bellman-Ford算法 1&#xff09;&#xff0c;深度或广度优先搜索算法&#xff08;解决单源最短路径&#xff09; 从起始结点开始访问所…

算法之几个常见的经典最短路径算法

目录 1. Dijkstra算法2. Floyd算法3. Bellman-Ford 算法 1. Dijkstra算法 是解单源最短路径问题的贪心算法。 有一向带权图 G (V, E)&#xff0c;包含右n个顶点&#xff0c;其中每条边的权是非负实数&#xff0c;定义数组 dist 为原点到G中各个顶点的距离&#xff0c;初始化为…

最短路径的四种算法

最短路径四种算法 1234FloydDijkstraBellman-Ford队列优化的Bellman-Ford 一&#xff0c;只有四行的算法——Floyd-Warshall 假设求顶点 V i Vi Vi到 V j Vj Vj的最短路径。弗洛伊德算法依次找从 V i Vi Vi到 V j Vj Vj&#xff0c;中间经过结点序号不大于 0 0 0的最短路径&…

最短路径算法

1.最短路径算法分为单源最短路径算法和多源最短路径算法 &#xff08;a&#xff09;单源最短路径算法&#xff0c;可以计算出从起点到任意一个起点的最短路径。 例如&#xff1a;Dijkstra算法 &#xff0c;SPFA算法 &#xff08;b&#xff09;多源最短路径算法&#xff0c;可…

哈夫曼树及其应用

1、哈夫曼树的基本概念 ---- 哈夫曼&#xff08;Huffman&#xff09;树又称作最优二叉树&#xff0c;它是n个带权叶子结点构成的所有二叉树中&#xff0c;带权路径长度最小的二叉树。 ---- “路径”就是从树中的一个结点到另一个结点之间的分支构成的部分&#xff0c;而分支…

哈夫曼树的C语言实现

什么是哈夫曼树 当用 n 个结点&#xff08;都做叶子结点且都有各自的权值&#xff09;试图构建一棵树时&#xff0c;如果构建的这棵树的带权路径长度最小&#xff0c;称这棵树为“最优二叉树”&#xff0c;有时也叫“赫夫曼树”或者“哈夫曼树”。 在构建哈弗曼树时&#xff0…

哈夫曼树的构建及编码

哈夫曼树的构建及编码 文章目录 哈夫曼树的构建及编码一、什么是哈夫曼树二、什么是哈夫曼编码三、怎么建哈夫曼树、求哈夫曼编码四、为什么哈夫曼编码能实现压缩 声明&#xff1a;关于文件压缩&#xff0c;不是本文的重点&#xff0c;本文只说明并讨论哈夫曼树的构建和编码&am…

如何构建一棵哈夫曼树

给一个数列{10,7,8,3,26,5,1},要求转成为一棵哈夫曼树。 分析思路以及图解&#xff1a; 第一步&#xff1a;将数列进行排序&#xff0c;按从小到大的顺序。最终结果为{1,3,5,7,8,10,26}&#xff0c;根据每个数值创建结点&#xff0c;组成结点数组 第二步&#xff1a;取出权值最…

哈夫曼树 (100分)哈夫曼树

4-1 哈夫曼树 (100分)哈夫曼树 第一行输入一个数n&#xff0c;表示叶结点的个数。 需要用这些叶结点生成哈夫曼树&#xff0c;根据哈夫曼树的概念&#xff0c;这些结点有权值&#xff0c;即weight&#xff0c;题目需要输出哈夫曼树的带权路径长度&#xff08;WPL&#xff09;。…

哈夫曼树的编码和解码

哈夫曼树的作用&#xff1a;在数据通信中&#xff0c;需要将传送的文字转换成二进制的字符串&#xff0c;用0&#xff0c;1码的不同排列来表示字符。例如&#xff0c;需传送的报文为“AFTER DATA EAR ARE ART AREA”&#xff0c;这里用到的字符集为“A&#xff0c;E&#xff0c…

哈夫曼树与哈夫曼编码

哈夫曼树 给定n个权值作为n个叶子结点&#xff0c;构造一棵二叉树&#xff0c;若带权路径长度达到最小&#xff0c;称这样的二叉树为最优二叉树&#xff0c;也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树&#xff0c;权值较大的结点离根较近。 树节点间的边…

【例题】哈夫曼树

【例1】由五个分别带权值为9&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;14的叶子结点构成的一棵哈夫曼树&#xff0c;该树的带权路径长度为_______________。 A、60 B、66 C、67 D、50 答案&#xff1a;C 解析&#xff1a; 关键点在于要学会如何构造哈夫曼树 已知有5…

哈夫曼树以及哈夫曼算法

目录 一、哈夫曼树的定义 二、哈夫曼树的特点 三、哈夫曼算法(构造哈夫曼树的方法) 四、哈夫曼树的构造过程 五、哈夫曼树构造算法的实现 一、哈夫曼树的定义 1、哈夫曼树:最优树即带权路径长度(WPL)最短的树 “带权路径长度最短”是在"度相同”的树中比较而得的结果…

哈夫曼树的绘制

数据结构之哈夫曼树绘制 本人还是一个年轻的程序猿(还是个学生)&#xff0c;请大家多多指教&#xff01; 哈夫曼树 已知权重绘制哈夫曼树 开始我的表演 Step 1. 已知权重&#xff1a;2&#xff0c;3&#xff0c;3&#xff0c;4&#xff0c;7&#xff0c;6 Step 2. 选取其中…

哈夫曼树 哈夫曼编码

哈夫曼树 哈夫曼树的定义&#xff1a;设二叉树具有 n 个带权值的叶节点&#xff0c;那么从根节点到各个叶节点的路径长度与相应叶节点权值的乘积的和&#xff0c;叫作二叉树的带权路径长度 WPL (Weighted Path Length)。具有最小带权路径长度的二叉树称为哈夫曼树 (Huffman Tr…

哈夫曼树(Huffman Tree)

定义 哈夫曼树又称最优二叉树&#xff0c;是一种带权路径长度最短的二叉树。所谓树的带权路径长度&#xff0c;就是树中所有的叶结点的权值乘上其到根结点的路径长度&#xff08;若根结点为0层&#xff0c;叶结点到根结点的路径长度为叶结点的层数&#xff09;。树的路径长度是…

哈夫曼树详解

一、哈夫曼树的介绍 Huffman Tree&#xff0c;中文名是哈夫曼树或霍夫曼树&#xff0c;它是最优二叉树。 定义&#xff1a;给定n个权值作为n个叶子结点&#xff0c;构造一棵二叉树&#xff0c;若树的带权路径长度达到最小&#xff0c;则这棵树被称为哈夫曼树。 这个定义里面涉…