图论:图的四种最短路径算法
目录:
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算法无非就是动态规划,状态转移方程为
核心代码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.在图论中一定要掌握好邻接表和邻接矩阵的建立。
基础知识充分了解之后,就是形成知识网络练习的过程了,希望阅读该文章后能让自己以及读者在图论方面有更深刻得到理解。图,何止是图!!!
图论:图的四种最短路径算法相关推荐
- 图的四种最短路径算法
本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,弗洛伊德算法,迪杰斯特拉算法,Bellman-Ford算法 1),深度或广度优先搜索算法(解决单源最短路径) 从起始结点开始访问所有的深度 ...
- 图的五种最短路径算法
本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,费罗伊德算法,迪杰斯特拉算法,Bellman-Ford 算法. 1)深度或广度优先搜索算法(解决单源最短路径) 从起点开始访问所有深度遍历 ...
- JVM之垃圾收集机制四种GC算法详解
JVM之四种GC算法详解 目录: 什么是GC? GC算法之引用计数法 GC算法之复制算法(Copying) GC算法之标记清除(Mark-Sweep) GC算法之标记压缩(Mark-Compact) ...
- 【JVM】四种GC算法(分代收集+三种标记算法)
目录 参考文章 四种GC算法 分代收集算法(理论) 标记清除算法 标记整理算法 标记复制算法 三种算法的优缺点 参考文章 JVM的4种垃圾回收算法.垃圾回收机制与总结_我是guyue,guyue就是我 ...
- php主要算法设计,四种排序算法设计(PHP)
标签 详细分析 /** * 四种排序算法设计(PHP) * * 1) 插入排序(Insertion Sort)的基本思想是: 每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当 ...
- php四种基础算法:冒泡,选择,插入和快速排序法
许多人都说 算法是程序的核心,一个程序的好于差,关键是这个程序算法的优劣.作为一个初级phper,虽然很少接触到算法方面的东西 .但是对于冒泡排序,插入排序,选择排序,快速排序四种基本算法,我想还是要 ...
- 深度搜索 java_java实现的深度搜索与广度搜索算法BFS,DFS以及几种最短路径算法...
java实现的深度搜索与广度搜索算法BFS,DFS以及几种最短路径算法 public class City { String name; int id; static int idCounter = ...
- 五种最短路径算法的总结(待更新)
最短路径算法: 1:Dijkstra 2:Floyd 3:Bellman-Ford 4:SPFA 5:A* 这五种最短路径算法初学的时候非常容易混淆,因为他们的松弛方法 ...
- 考研数据结构 图的四种算法 ---- 来自天勤高分笔记
/*******************************最小代价生成树之普利姆算法思想:-----------贪心算法思想从图中任意取出一个顶点,把它当成一棵树,然后从与这棵树相邻接的边中选取 ...
最新文章
- 【组队学习】【23期】Datawhale深度推荐模型
- Python趣味打怪:60秒学会一个例子,147段简单代码助你从入门到大师 | 中文资源...
- 数组公式基本功修炼之数组扩充
- 对物联网操作系统特征和定位的思考
- 使用Duilib做桌面应用总结
- django crm 03
- C++/C--vector初始化与赋值【转载】
- 摩根士丹利 Morgan Stanley 2008校园招聘已经正式开
- Sampled Softmax,你真的会用了吗?
- 汇丰银行是哪个国家的
- 二年级课程表(4月18日-4月22日)
- JVM源码分析之wait()和notify()
- 品味奢华 匠心独韵——飞利浦Fidelio T1设计与声音的哲学
- LINUX基础知识笔记全
- 学习记录:Unity点击屏幕生成随机UI花朵
- 【解决方法】Win10还原默认打开方式图标
- 华为智慧森林防火监测预警解决方案
- python评价指标_详解分类评价指标和回归评价指标以及Python代码实现
- CVE-2020–9854漏洞攻击链分析
- 读王竹峰老师 《一个数据库十年老兵的思考与总结》 有感