数据结构:图论(二)最短路径

本文介绍了图论中的最短路径问题,包括单源最短路径的Dijkstra算法和多源最短路径的Floyd算法。通过实例解析算法原理,详细阐述了如何在无权图和有权图中寻找最短路径,并提供了相应的C++代码实现。
摘要由CSDN通过智能技术生成

在这里插入图片描述

起床继续写图论!!!!!!!!!!!!!!

最短路径

分类

单源最短路径问题:从某固定源点出发,求其 到所有其他顶点的最短路径
分为有向图和无向图
多源最短路径问题:求任意两顶点间的最短路 径

先熟悉无权图的最短路,方便后面理解有权图的最短路径问题。
无权图的最短路算法
在这里插入图片描述
给定某一个顶点,求该顶点到其余顶点的最短路径。例给定V3,要求出V3到个顶点的路径最短长度,即为
在这里插入图片描述
1、首先要确定对图的遍历方式,考虑到是对单顶点到各顶点的最短路径,所以应该是对这个顶点及他的所有邻接点,那么我们应该采用广度优先对这个顶点开始遍历
2、必须应该有一个记录路径长度的数组以及记录经过一个顶点的上一个顶点,通过该数组就可以往前推出整条路径所经过的顶点,于是我们引入dist[]、path[]来分别记录

在这里插入图片描述
3、如何初始化dist数组跟path数组?
因为dist数组可以在记录路径长度的同时记录结点是否被访问,这样可以避免在开设一个数组记录。其实在无权图中对dist数组默认值为-1或∞,并没有特别的要求,只要能够辨认该节点是否被访问过即可。

贴上单源最短路的源码,建图的部分在上一篇文章就讲过了,这里就不给源码了。

void init(){
   
	for(int i = 0;i<MAXVertextNum;i++){
   
		dist[i] = -1;
		path[i] = -1;
	}
}
//初始化一个图
LGraph CreateGraph( int VertexNum ){
   
	//如果要求单源最短路,则需要初始化dist和path数组
	init();
	LGraph Graph = new GNode;
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	//将所有结点的头指针置空
	for(Vertex V = 0 ; V < Graph->Nv ; V++){
   
		Graph->G[V].firstEdge = NULL;
	}
	
	return Graph;
}
//单源最短路算法,dist[]和path[]初始为-1
void Unweighted( LGraph Graph , int dist[] , int path[] , Vertex S){
   
	queue<Vertex> Q;
	dist[S] = 0;
	//初始化源点
	Q.push(S);
	while(!Q.empty()){
   
		S=Q.front();
		Q.pop();
		//遍历临界点
		for(ptrToAdjNode cur = Graph->G[S].firstEdge;cur;cur = cur->Next){
   
			//dist数组不仅能够记录长度,并且能够记录该结点是否被遍历过
			if(dist[cur->AdjV] == -1){
   
				dist[cur->AdjV] = dist[S] + 1;
				path[cur->AdjV] = S;
				Q.push(cur->AdjV);
			}
		}
	}
}

其实理解了BFS遍历,这个算法就很好理解了,因为他们两个长得几乎一样

//BFS遍历图
void BFS(LGraph Graph,Vertex V,void (*Visit)(Vertex) ){
   
	if(V > Graph->Nv){
   
		return ;
	}
	/* 以V为出发点对Graph进行BFS搜索 */
	queue<Vertex> Q; 
	Visit(V);
	Visted[V] = true;
	Q.push(V);
	while(!Q.empty()){
   
		V = Q.front();
		Q.pop();
		for(ptrToAdjNode cur = Graph->G[V].firstEdge ; cur ; cur = cur->Next){
   
			if(!Visted[cur->AdjV]){
   
				Visit(cur->AdjV);
				Visted[cur->AdjV] = true;
				Q.push(cur->AdjV);
			}
		}
	}	
}
//单源最短路算法,dist[]和path[]初始为-1
void Unweighted( LGraph Graph , int dist[] , int path[] , Vertex S){
   
	queue<Vertex> Q;
	dist[S] = 0;
	//初始化源点
	Q.push(S);
	while(!Q.empty()){
   
		S=Q.front();
		Q.pop();
		//遍历临界点
		for(ptrToAdjNode cur = Graph->G[S].firstEdge;cur;cur = cur->Next){
   
			//dist数组不仅能够记录长度,并且能够记录该结点是否被遍历过
			if(dist[cur->AdjV] == -1){
   
				dist[cur->AdjV] = dist[S] + 1;
				path[cur->AdjV] = S;
				Q.push(cur->AdjV);
			}
		}
	}
}

对比就能发现,只是把存储是否访问过的Visit数组换成了记录长度的dist数组,并存储了经过的每个顶点所对应的上一个顶点
Dijkstra算法
先给抽象的数学概念:
1、令S={源点s + 已经确定了最短路径的顶点vi}
2、对任一未收录的顶点v,定义dist[v]为s到v的最短路径长度,但该路径仅经过S中的顶点。即路径 {s->(vi属于S)->v}的最小长度。

若路径是按照递增(非递减)的顺序生成的,当时会对这个概念其实是有疑问的,为什么真正的最短路必须只经过S中的顶点?这个问题希望大家可以通过这个图归纳得出:真正的最短路径必定是经过S中的顶点。这里也是解决这个算法的核心。

在这里插入图片描述
主要应该解决的问题:
1、如何确保路径是最小的并且能够将顶点对应的邻接点收录进来?应该如何初始化dist数组?
在有权图中,dist数组的值就不能随意地定为-1或∞。 如果s到w有直接的边,则dist[w]=<s,w>的权重;否则dist[w]定义为∞,因为这里必须解决的是收录路径最小的邻接点。如果dist出现负数的时候就会成环了
2、 在增加一个V进入S时可能会影响到另一个顶点的dist值,那么应该怎么处理?
这道题能帮你解决这个问题

如果收录v使得s到w的路径变短,则:
A.w应该先于v被收录
B.s到w的路径一定经过v,并且v到w有一条边
C. s到w的路径一定经过v,但是v不一定直接跟w相连

这个题选B,如果选对了就说明你已经解决了这两个问题了。
给出每一步dist和path的变化
从V0开始,只有V1跟V3相连。对应的初始化
在这里插入图片描述

在这里插入图片描述

接着从路径最小的V1出发,找它的临界点V0->V2出现更短的路径,对dist[V2]做出更新,dist跟path更新为
在这里插入图片描述
给V1打上已遍历的标记,接着开始从下一个最小的dist开始,找到V2结点,有新的路径到达V4,但是dist[V4]原来的值更小,所以不改变
在这里插入图片描述
这一步更新为

在这里插入图片描述
接着找到没有遍历结点中dist最小的顶点V3,都是不影响的路径长度

在这里插入图片描述
这一步没有更新:

在这里插入图片描述
V3已被遍历,再找到dist最小的顶点V5,出现更小,dist\path更新
在这里插入图片描述

V5已经被遍历,找到dist最小的V4,出现更小继续更新
在这里插入图片描述
接下来仍然要遍历最后一个顶点V6,因为不同的图会存在更小的dist,所以对最后一个顶点V6作最后的遍历,最终的得到dist 跟path如下图:
在这里插入图片描述
搞清楚每一个步骤之后,就是代码的实现:
1、真正的路径必定只经过S中的顶点
2、每次未收录的顶点中选一个dist最小的,媚增加一个V进入S,会影响其他的顶点dist,需要进行判断

dist[V] + Graph->G[V][W] < dist[W]?dist[W] = dist[V] + Graph->G[V][W]:continue

这里dist无法作是否收录判断,引入一个collect对收录顶点的判断。
FindMinDist函数:每次未收录的顶点中选一个dist最小的顶点 。函数实现

//邻接矩阵存储  有权图的单元最短路算法
//返回未被收录顶点的最小值
Vertex FindMinDist( MGraph Graph, int dist[], bool collected[] ){
   
	Vertex MinV;
	int MinDist = INFINIIY;
	//第一次起点和临界点已经构建起dist数组,必有dist最小值
	//之后先返回V值,对dist继续构建,所以dist数组的值越来越多,每次只需要在最短路径上进行延申
	//遍历循环得到是否得到最小且未被收录的dist路径,找到对应的结点
	for(Vertex V = 0;V < Graph->Nv ; V++ ){
   
		//1、有,且未被收录则返回该值
		if(collected[V] == false && dist[V] < MinDist){
   
			MinDist = dist[V];
			MinV = V;
		}
	}
	if(<
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值