最短路径算法详解

article/2025/8/25 3:00:25

前言:

最短路径算法:用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

例子:

暑期,你想要出门去旅游,但是在你出发之前,你想知道任意两个城市之间的最短距离。

给出地图:

                                

给出数据:

4 8

1 2 2

1 3 6

1 4 4

2 3 3

3 1 7

3 4 1

4 1 5

4 3 12

把数据转换成邻接矩阵:

1.Floyd-Warshall算法

思路:

在邻接矩阵中,依次把每一个点作为中间点,进行中转;

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int inf=99999999;
const int N=101;int map[N][N];//存储城市之间的距离;
int n,m;
int aa,bb,cc;void init()//初始化;
{for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j)map[i][j]=0;elsemap[i][j]=inf;}}
}int main()
{while(~scanf("%d%d",&n,&m)){init();for(int i=0;i<m;i++){scanf("%d%d%d",&aa,&bb,&cc);map[aa][bb]=cc;}for(int k=1;k<=n;k++)//中转点;{for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(map[i][j]>map[i][k]+map[k][j])map[i][j]=map[i][k]+map[k][j];}}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){printf("%10d",map[i][j]);}printf("\n");}}return 0;
}

2.Dijkstra算法----单源最短路

思路:

1.将所有的顶点分成两个部分:已知最短路程的顶点集合P和未知的最短路径的顶点集合Q。最开始,已知最短路径的定点集合P中只有源点一个顶点。我们这里用一个book数组来记录哪些点在集合中。例如,如果book[i]为1,代表这个顶点在集合P中,否则不在集合P中,在集合Q中。

2.设置源点s到自己的最短路径为0,即dis[s]=0。若存在有源点能直接到达的顶点 i,则把dis[i]设置为map[s][i]。同时,把所有其他(源点不能直接到达的)顶点的最短路径设为无穷大;

3.在集合Q的所有顶点中选择一个离源点s最近的顶点u(即dis[u]最小),加入到集合P中,并考察所有以u为起点的边,对每一条边进行松弛操作。例如,存在一条边从u到v的边,那么可以通过将边(u,v),添加到尾部来拓展一条从s到v的路径,这条路径的长度是dis[u]+map[u][v]。如果这个值比以前已知的dis[v]的值要小,那么我们就可以用新值来替代当前的dis[v];

4.重复第3步,如果集合Q为空,算法结束。最终dis数组中存储的值就是源点到各个点的距离;

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int inf=99999999;
const int N=101;int map[N][N];//存储城市之间的距离;
int book[N];//记录点是否已经最短路径中;
int dis[N];//记录到源点的距离;
int n,m;
int aa,bb,cc;
int minn,u;void init()//初始化;
{for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j)map[i][j]=0;elsemap[i][j]=inf;}}
}void Dijkstra()
{for(int i=1;i<n;i++){minn=inf;for(int j=1;j<=n;j++)//选取最小的顶点加入最短路径;{if(book[j]==0&&dis[j]<minn){minn=dis[j];u=j;}}book[u]=1;for(int v=1;v<=n;v++){if(map[u][v]<inf)//松弛;{if(dis[v]>dis[u]+map[u][v])dis[v]=dis[u]+map[u][v];}}}for(int i=1;i<=n;i++)printf("%d ",dis[i]);printf("\n");return;
}int main()
{while(~scanf("%d%d",&n,&m)){init();for(int i=0;i<m;i++){scanf("%d%d%d",&aa,&bb,&cc);map[aa][bb]=cc;}memset(book,0,sizeof book);for(int i=1;i<=n;i++)//记录各个点到源点的距离;dis[i]=map[1][i];book[1]=1;Dijkstra();}return 0;
}

3.Bellman-Ford----解决负权边

思路:

Dijkstra算法虽然好,但是它不能解决带有负权边的图,但是Bellman-ford算法可以解决这个问题。

Bellman-ford算法很简单,核心代码只有4行,并且可以完美的解决带有负权边的图,代码如下:

for(int k=1; k<n; k++)for(int i=1; i<=m; i++)if(dis[v[i]]>dis[u[i]]+w[i])dis[v[i]]=dis[u[i]]+w[i];

上面的代码中,外层循环一共循环了n-1遍(n是定点的个数),内层循环一共循环了m次(枚举了每一条边);

dis[ ]数组中存储的是源点到其余各个点的最短的距离;

u,v,w三个数组中存储边的信息:u是边的起点,v是边的终点,w是边的距离;

if(dis[v[i]]>dis[u[i]]+w[i])dis[v[i]]=dis[u[i]]+w[i];

 上面说了,dis[ ]数组中存储的是源点到其余各点的距离。那么这两行代码的意思是:试试能不能通过边u[i]-->v[i]这条边,把源点到v[i]的距离变小。

图例:

          

初始化后的结果:

   

第一轮松弛后的结果:

 

第二轮松弛后的结果:

 

第三轮松弛后的结果:

第四轮松弛后的结果:

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int N=1010;
const int inf=99999999;int dis[N];
int k,n,m;
int u[N],v[N],w[N];void init()//初始化;
{for(int i=1; i<=n; i++)dis[i]=inf;
}void Bellman_Ford()
{dis[1]=0;for(int k=1; k<n; k++){for(int i=1; i<=m; i++){if(dis[v[i]]>dis[u[i]]+w[i])dis[v[i]]=dis[u[i]]+w[i];}}for(int i=1; i<=n; i++)printf("%d ",dis[i]);printf("\n");return ;
}int main()
{scanf("%d%d",&n,&m);init();for(int i=1; i<=m; i++){scanf("%d%d%d",&u[i],&v[i],&w[i]);}Bellman_Ford();return 0;
}

当然,Bellman-Ford算法也可以判断负权边,因为没有负权边的图中,进行n-1轮松弛后就该结束了,所以当有n-1轮松弛后就应该没有变化了,已经是最短的距离了;如果n-1轮松弛后还是能够改变最小的距离,那么就是有了负权边;

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int N=1010;
const int inf=99999999;int dis[N];
int k,n,m;
int u[N],v[N],w[N];
int check,flag;void init()//初始化;
{for(int i=1; i<=n; i++)dis[i]=inf;
}void Bellman_Ford()
{dis[1]=0;for(int k=1; k<n; k++){check=0;for(int i=1; i<=m; i++){if(dis[v[i]]>dis[u[i]]+w[i]){dis[v[i]]=dis[u[i]]+w[i];check=1;}}if(check==0)//一轮下来没有变化,break;//说明dis数组已经是最短距离了,直接结束就可以了;}flag=0;//没有负权回路的图在n-1轮松弛后就应该结束了;for(int i=1; i<=m; i++)//如果在n-1轮后再进行一轮还是能松弛,{                      //那么就是有负权回路;if(dis[v[i]]>dis[u[i]]+w[i])flag=1;}if(flag==1)printf("此图有负权回路!\n");else{for(int i=1; i<=n; i++)printf("%d ",dis[i]);printf("\n");}return ;
}int main()
{scanf("%d%d",&n,&m);init();for(int i=1; i<=m; i++){scanf("%d%d%d",&u[i],&v[i],&w[i]);}Bellman_Ford();return 0;
}

4.SPFA算法(Bellman-Ford算法的队列优化)

思路:

每一次仅仅对最短路估计值发生变化的顶点的所有出边执行松弛操作;

把发生变化的顶点放入队列中进行维护;

每次选取队首的顶点u,对u的所有出边进行松弛操作,操作结束后就把顶点u出队列,再重复上面的操作;

需要注意的是,对于同一个顶点没必要同时多次存在于队列中,所以,我们需要一个队列来判重;

当然,我们要对于一个顶点的所有出点进行松弛操作,所以对于图的存储我们要改成用邻接表存储,不能再用邻接矩阵了;

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int N=1010;
const int inf=99999999;int n,m;
int u[N],v[N],w[N];
int first[N],next[N];
int dis[N];//记录源点到各个点的最短距离;
int book[N];//记录哪个顶点进入到了队列中;
int que[N];//队列;
int head,tail,k;void init()
{for(int i=1; i<=n; i++)dis[i]=inf;memset(book,0,sizeof book);memset(first,-1,sizeof first);
}void solve()
{head=tail=1;que[tail]=1;tail++;book[1]=1;while(head<tail){k=first[que[head]];while(k!=-1){if(dis[v[k]]>dis[u[k]]+w[k]){dis[v[k]]=dis[u[k]]+w[k];if(book[v[k]]==0)//没有在队列中;{que[tail]=v[k];tail++;book[v[k]]=1;}}k=next[k];}book[que[head]]=0;head++;}return ;
}void print()
{for(int i=1; i<=n; i++)printf("%d ",dis[i]);printf("\n");
}int main()
{scanf("%d%d",&n,&m);init();//初始化;dis[1]=0;for(int i=1; i<=m; i++){scanf("%d%d%d",&u[i],&v[i],&w[i]);next[i]=first[u[i]];//建立邻接表;first[u[i]]=i;}solve();//解决问题;print();//输出结果;return 0;
}

 


http://chatgpt.dhexx.cn/article/9B7lA5IN.shtml

相关文章

基于java最短路径算法的简单实现

一、我们先画一个可达路径图表&#xff1a; -1表示不可直达&#xff0c;0表示自己&#xff0c;其他整数表示长度或者权重 ABCDA012-1B-1015C-1-101D-1-1-10 用图形可表示为&#xff1a; 二、算法思路 1、先列出所有保存所有节点到可达节点的路径二维表 2、逐步寻找从起始节点到…

最短路径算法及应用

乘汽车旅行的人总希望找出到目的地的尽可能的短的行程。如果有一张地图并在图上标出每对十字路口之间的距离&#xff0c;如何找出这一最短行程&#xff1f;  一种可能的方法就是枚举出所有路径&#xff0c;并计算出每条路径的长度&#xff0c;然后选择最短的一条。那么我们很…

关于最短路径算法的理解

“最短路径算法:Dijkstra算法&#xff0c;Bellman-Ford算法&#xff0c;Floyd算法和SPFA算法等。​从某顶点出发&#xff0c;沿图的边到达另一顶点所经过的路径中&#xff0c;各边上权值之和最小的一条路径叫做最短路径。” 我们解决最短路径问题&#xff0c;常用的是Dijkstra…

最短路径算法-----Dijkstra迪杰斯特拉算法

最近巩固一下算法&#xff0c;提高自己内力&#xff0c;网上看到查看到这篇介绍很详细的《Dijkstra迪杰斯特拉算法》&#xff0c;在这里转载记录一下。 1 前言 本章介绍迪杰斯特拉算法。和以往一样&#xff0c;本文会先对迪杰斯特拉算法的理论论知识进行介绍&#xff0c;然后给…

Dijkstra 最短路径算法 Python 实现

原文链接 问题描述 使用 Dijkstra 算法求图中的任意顶点到其它顶点的最短路径&#xff08;求出需要经过那些点以及最短距离&#xff09;。 以下图为例&#xff1a; 算法思想 可以使用二维数组来存储顶点之间边的关系 首先需要用一个一维数组 dis 来存储 初始顶点到其余各个顶…

神经网络最短路径算法,最短路径算法的原理

节约里程法求解最短路问题 你只要记住2点之间直线最短。节约里程法是用来解决运输车辆数目不确定的问题的最有名的启发式算法。1、节约里程法优化过程分为并行方式和串行方式两种。 核心思想是依次将运输问题中的两个回路合并为一个回路&#xff0c;每次使合并后的总运输距离…

最短路径算法及Python实现

最短路径问题 在图论中&#xff0c;最短路径问题是指在一个有向或无向的加权图中找到从一个起点到一个终点的最短路径。这个问题是计算机科学中的一个经典问题&#xff0c;也是许多实际问题的基础&#xff0c;例如路线规划、通信网络设计和交通流量优化等。在这个问题中&#…

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

目录&#xff1a; 1.DFS&#xff08;单源最短路径算法&#xff09; 例题1&#xff1a; DFS题目分析&#xff1a; 代码DFS&#xff1a; 2.Floyed&#xff08;时间复杂度On^3&#xff09; 1.应用场景&#xff1a; 2.解析算法&#xff1a; 核心代码1&#xff1a; 我的笔…

图的五种最短路径算法

本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,费罗伊德算法,迪杰斯特拉算法,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;。…