图的应用—求解最短路径(BFS、Dijkstra和Floyd算法)
BFS算法虽然可以求解最短路径问题,但是需要注意的是该算法只能求解非带权图的单源最短路径问题,或者说带权值相同且为1的图单源最短路径问题。
1、图的邻接矩阵存储结构定义
#define MaxVerNum 100 //顶点数目的最大值
typedef struct{char Vex[MaxVerNum]; //顶点表int Edge[MaxVerNum][MaxVerNum]; //邻接矩阵,边表int vexnum,archnum; //图的当前顶点数和弧数
}Graph;
2、图的邻接表存储结构定义
#define MaxVerNum 100 //顶点数目的最大值
typedef struct ArcNode{ //边表结点 int adjvex; //该弧所指向的顶点的位置struct ArcNode *next; //指向下一条弧的指针 int num; //图的权值
}ArcNode;
typedef struct VNode{ //顶点表信息 char data; //顶点信息 ArcNode *first; //指向第一条依附于该顶点的弧的指针
}VNode,AdjList[MaxVerNum];
typedef struct{AdjList vertices; //邻接表int vexnum,arcnum; //图的顶点数和弧数
}Graph;
3、BFS算法求解非带权图的单源最短路径
//BFS算法求解非带权图单源最短路径问题
void BFS_MIN_Distance(Graph G,int u) //求到u的单源最短路径
{for(int i=0;i<G.vexnum;i++){d[i]=INF; //初始化路径长度path[i]=-1; //初始化到达每个顶点的前驱结点 } visit[u]=true;d[u]=0; //自身到自身的距离为0 EnQueue(Q,u);while(!isEmpty(Q)){DeQueue(Q,u);for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){if(!visit[w]){visit[w]=true;d[w]=d[u]+1; //路径长度加1 path[w]=u; //最短路径是从u到w EnQueue(Q,w); //w入队 }}}
}
由于BFS算法的应用局限,所以对于带权值(正值)的图, 我们需要求解其单源最短路径的时候,就可以使用Dijkstra算法。
单源最短路径是指:图中某一顶点到其他各顶点的最短路径。
4、Dijkstra算法的定义
Dijsktra算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层扩展,直到扩展到终点为止。在无向图G=(V,E)中,假设每条边E[i]的长度为w[i],找到由顶点v0到其余各点的最短路径。
5、Dijkstra算法的基本思想
1、把路径长度递增次序产生最短路径算法。
2、把V分为两组:
S:已求出最短路径的顶点的集合,初始时S中只有一个源点,以后每求得一条最短路径就将其加入到集合S中,直到所有顶点都加入到S中;
V-S=T:尚未确定最短路径的顶点集合;
3、将T中顶点按最短路径递增的次序加入到S中,保证:从源点V0到S中各顶点的最短路径长度都不大于从V0到T中任何顶点的最短路径长度。
4、每个顶点对应一个距离值:
S中顶点:从V0到此顶点的最短路径长度;
T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度;
6、具体实现过程
首先将其全部初始化为无穷大(除了源点到源点之外):
在表中选择一个路径最短的点a,从a出发,a到自己的距离是0,它往外有三条边,更新a到b,c,d的最短距离,此时a被标记,则此后a都不会再被访问,此时该表更新为:
再在这张表中选择一个最短距离,是A->B,选出B作为新的起点,它有两条边分别是到达C,E,由于从B到C的距离是2,则从源点经过B到达C的最短距离是3,比之前的要小,所以更新最短距离,这时B也被标记,B不会再被访问。
再在这张表中选择一个最短距离,从AàC,则选择C点作为新的起点,从C出发有2条边,分别到达D,E,由于从A经过C到E的最短距离是23,小于之前的51,所以更新最短距离,从A经过C到达D的最短距离是8,小于之前的10,更新最短距离,则此时C被标记,C之后便不会再被访问。
再在这张表中选择一个最短距离,从A->D,所以选择D作为新的起点,从D只有一条边,到达E,则此时从A经过B,C,D到达E的最短距离是11,小于之前的23,所以更新最短距离,则此时D被标记,D不会再被访问。
再在这张表中选择一个最短距离,只有A->E,由于它并没有没访问的边,所以循环跳出,整个遍历结束!
所以这个有向图的单源最短路径是0,1,3,8,11。
再需要更新最短路径的时候,使用数组tar记录下到达当前顶点的前一个顶点,也就是最佳路径的弧尾和弧头。然后使用深度优先遍历递归查找每个顶点到源点的最短路径。
7、给出Dijkstra算法求解单源最短路径的实例
【问题描述】
给定一个n个顶点,m条有向边的带非负权图,请计算从s出发,到图中各点的距离。
【输入输出及示例】
输入:顶点数n,边数m,源点s,m条边以(p,q,weight)形式输入,分别表示弧尾、弧头和弧长;
输出:源点到每个点的距离,同时输出源点到每个点的路径。
示例1:
输入:顶点数:7,边数:10,源点:1
弧尾、弧头、弧长为:
1 2 13
1 3 8
1 5 30
1 7 32
2 6 9
2 7 7
3 4 5
4 5 6
5 6 2
6 7 17
输出:源点到顶点1的最短路径长度是:0
源点到顶点1的最短路径是:1
源点到顶点2的最短路径长度是:13
源点到顶点2的最短路径是:1 2
源点到顶点3的最短路径长度是:8
源点到顶点3的最短路径是:1 3
源点到顶点4的最短路径长度是:13
源点到顶点4的最短路径是:1 3 4
源点到顶点5的最短路径长度是:19
源点到顶点5的最短路径是:1 3 4 5
源点到顶点6的最短路径长度是:21
源点到顶点6的最短路径是:1 3 4 5 6
源点到顶点7的最短路径长度是:20
源点到顶点7的最短路径是:1 2 7
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0X3F3F3F3F//使用链式存储边,建图
struct node{int begin; //边的始点 int end; //边的终点 int cost; //边的代价 int next; //当前边的下一条相邻边
}e[1010]; int head[1010]; //head[i]存储与该点相邻的边
int dis[1010]; //存放源点到每个点的最短距离
int visit[1010]; //标记哪些点被访问,访问的点被设为1
int tar[1010]; //追踪最短路径void dfs(int s,int v) //输出最短路径
{if(s==v) //递归到源点,输出源点 {cout<<s<<" ";return ;}dfs(s,tar[v]); //一直寻找到达该点的路径 cout<<v<<" ";
}
int main()
{int n,m,s;cout<<"请输入顶点的个数、边的条数以及源点的位置: ";cin>>n>>m>>s;cout<<"请输入每条边的起点、始点和代价:"<<endl;for(int i=1;i<=m;i++) //存储边,建图 {cin>>e[i].begin>>e[i].end>>e[i].cost;int from=e[i].begin; //记录当前是从哪个点发出 e[i].next=head[from]; //存储该点的下一条边 head[from]=i; // 更新与该点的相邻的最后一条边(也就是当前边) } for(int i=1;i<=n;i++) //初始化,先将每个距离设置为最大值 {dis[i]=INF;} int begin=s;dis[begin]=0; //自己到自己的最短距离为0 while(visit[begin]!=1) //对每个点进行查找,直至所有点被标记完 {visit[begin]=1; //没被访问,那就访问 for(int i=head[begin];i!=0;i=e[i].next) //依次访问与当前起点相邻的边{if(dis[e[i].end]>dis[e[i].begin]+e[i].cost) //更新不同起点到达该点的最短距离 {dis[e[i].end]=dis[e[i].begin]+e[i].cost;tar[e[i].end]=e[i].begin; //存储当前点的前一个点 }} int min1=INF;for(int i=1;i<=n;i++) //每一次都寻找所有还没被标记的点中距离最小的点{if(visit[i]!=1&&dis[i]<min1){min1=dis[i];begin=i; //以距离最小的点为新的起点,从当前点出发 }}} cout<<"源点到每个点的最短路径长度:"<<endl;for(int i=1;i<=n;i++) {cout<<"源点到顶点"<<i<<"的最短路径长度为:"<<dis[i]<<endl;if(dis[i]==INF) //说明不可达{cout<<"该点不可达!"<<endl<<endl;} else{cout<<"源点到顶点"<<i<<"的最短路径是:";dfs(s,i);cout<<endl<<endl;}}return 0;
}
需要注意的是:Dijkstra算法虽然能求解带权图的单源最短路径问题,但是其权值只能是非负值,对于负权值该算法不一定能得到正确结果。
8、Floyd算法求各顶点之间最短路径问题
Floyd算法其实是基于动态规划的思想,在原路径中尝试加入顶点k作为中间顶点,得到的新路径与原路径长度比较,得到最短路径。
#define Maxsize 100
int path[Maxsize][Maxsize]; //记录某一顶点到另一顶点的中转矩阵
int A[Maxsize][Maxsize]; //记录从顶点i到顶点j的路径长度
//初始化准备工作,初始化矩阵A和path
for(int k=0;k<n;k++) //列举中转点
{for(int i=0;i<n;i++) //遍历整个矩阵的行和列 {for(int j=0;j<n;j++){if(A[i][j]>A[i][k]+A[k][j]){A[i][j]=A[i][k]+A[k][j]; //更新最短路径 path[i][j]=k; //记录中转点 }}}
}
Floyd算法的时间复杂度为O(|V|^3),空间复杂度为O(|V|^2)。该算法可以解决图中带负权值的问题,但是其不允许包含带负权值的边组成的回路,因为这种图有可能不存在最短路径。
图的应用—求解最短路径(BFS、Dijkstra和Floyd算法)相关推荐
- 数据结构之图的应用:最短路径(Dijkstra、Floyd)
图的应用:最短路径 思维导图: 最短路径的定义: BFS算法:(无权单源最短路径) Dijkstra算法:(无权单源.带权单源) Dijkstra算法原理: 算法实现中的辅助数组: 例: 如何通过pa ...
- 最短路径的求解方法(Dijkstra、Floyd算法)
用带权的有向图表示一个交通运输网,图中: 顶点--表示城市 边--表示城市间的交通联系 权--表示此线路的长度或沿此线路运输所花的时间或费用等 问题:从某顶点出发,沿图的边到达另一顶点所经过的路径中的 ...
- 数据结构(六):图的概念、存储方式、基本操作、最小生成树、最短路径、有向无环图、关键路径 | Prim、Kruskal算法 | BFS、Dijkstra、Floyd算法 | 拓扑排序 | 求关键路径
文章目录 第六章 图 一.图 (一)图的定义 (二)图逻辑结构的应用 (三)无向图.有向图 (四)简单图.多重图 (五)顶点的度.入度.出度 (六)顶点-顶点的关系描述 (七)连通图.强连通图 (八) ...
- 图论算法之最短路径(Dijkstra、Floyd、Bellman-ford和SPFA)
图论算法之最短路径(Dijkstra.Floyd.Bellman-ford和SPFA) 1.图论最短路径概述 图论算法为了求解一个顶点到另一个顶点的最短路径,即如果从图中某一顶点(称为源点)到达另一顶 ...
- 图的单源最短路径:Dijkstra算法实现
本文介绍的是图的非负权值的单源最短路径问题.问题的提出是,对于有权图D,t提供源点v,要找到从v到其他所有点的最短路径,即单源最短路径问题,在本文中,解决这一问题,是普遍比较熟悉的Dijkstra算法 ...
- 最短路径算法——Dijkstra and Floyd算法
一. 前言: 这个古老的算法应该耳熟能详了吧,但是我自从从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上很多的资料 ...
- 最短路径模板+解析——(FLoyd算法)
对于无权的图来说: 若从一顶点到另一顶点存在着一条路径,则称该路径长度为该路径上所经过的边的数目,它等于该路径上的顶点数减1. 由于从一顶点到另一顶点可能存在着多条路径,每条路径上所经过的边数可能不同 ...
- 图的单源最短路径(Dijkstra算法)
单源最短路径问题 如果从图中某一顶点(源点)到达另一顶点(终点)的路径可能不止一条,如何找到一条路径使得沿此路径各边上的权值总和达到最小. Dijkstra算法由来 迪杰斯特拉算法(Dijkstra) ...
- 【最短路径问题笔记】Floyd算法求多源最短路径问题
代码: 使用邻接矩阵建图 使用邻接矩阵存放最短距离 #include<iostream> #include<cstring> using namespace std; co ...
最新文章
- 在家搭建大数据分布式计算环境!
- Netty的引用计数对象
- 3.4.1 计算机网络之流量控制(停止-等待协议、滑动窗口、后退N帧协议GBN、选择重传协议SR)、滑动窗口、可靠传输机制
- POJ - 1741 Tree(点分治模板题)
- Android开发之通过ImageView名称从文件夹拿到ImageView的Drawable对象的方法
- TensorFlow基本计算单元——变量
- Offer年薪低于25W全额退款|阿里、腾讯内推快艇《全链路大数据分析工程师》课程招生简章...
- iframe内部内容在浏览窗口位置固定的问题
- 用计算机编码原理解释,编码原理
- 读取MySQL二进制文件_MYSQL: mysqlbinlog读取二进制文件报错read_log_event()
- PopupWindow点击空白区域消失
- Python基础之字符串
- Window XP驱动开发(十一) USB2.0 芯片CY7C68013A+FPGA实现的高速传输系统设计(软件及硬件)
- word页码任意设置,如显示在页脚外侧、横版页面要求显示在左侧或右侧
- 【文摘】2008年度_Atom处理器
- C语言 猜数游戏 首先由计算机产生一个随机数,并给出这个随机数所在的区间,然后有游戏者猜测这个数。猜中游戏结束,并可以重新挑战,猜错重新给出提示,如果猜测超过八次游戏失败。
- 【金猿人物展】树根互联COO黄路川:从“灯塔工厂”到“数字领航”,工业互联网操作系统助推新型工业化...
- 2-1:编写一个Java应用程序,输出俄文字母表。
- 技嘉主板命名规则是什么
- Python程序设计基础(第2版)by董付国 习题答案