栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

数据结构 Dijkstra算法 改良版 多条最短路径问题通用解决方案——C语言实现

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据结构 Dijkstra算法 改良版 多条最短路径问题通用解决方案——C语言实现

Dijkstra算法是图论中的一个重要的基础算法,是解决有权图单源最短路径问题的核心算法,大家需要牢固掌握;

我们先来回顾下Dijkstra算法:WeightType dist[] 是记录最短路径值的数组,Vertex path[] 是记录最短路径下每个顶点的前驱顶点 的数组

结构体设计说明

typedef int Vertex;     //顶点
typedef int WeightType; //边权
typedef int DataType;   //顶点数据(点权)

//边
typedef struct _Edge{
	Vertex V1,V2;
	WeightType Weight;
}Edge;

//邻接矩阵存储的图
typedef struct _MGraph{
	int Nv;               //顶点数
	int Ne;               //边数
	WeightType G[MaxVertexNum][MaxVertexNum]; // 邻接矩阵
	DataType Data[MaxVertexNum]; //存顶点的数据 很多情况下,顶点无数据,此时不用出现
}MGraph;

Dijkstra


bool Dijkstra( MGraph* Graph,int dist[],int path[],Vertex S )
{
	int collected[MaxVertexNum];
	Vertex V,W;
	
	
	for(V=0;VNv;V++){
		
		dist[V] = Graph->G[S][V];
		if(dist[V]Nv;W++){ //对图中的每个顶点W
			
			
			if(collected[W])==false &&Graph->G[V][W]G[V][W] < 0){ //若有负边
					return false;       //Disjkstra算法无法解决,返回错误标记
				}
				
				if(dist[V]+Graph->G[V][W] < dist[W]){
					
					dist[W] = dist[V] + Graph->G[V][W]; //修正dist[W]
					path[W] = V;                        //修正S到W的路径
				}
			}
		}
	} 
	
	return true; 
}

Vertex FindMinDist( MGraph* Graph,int dist[],int collected[] )
{
	Vertex MinV,V;
	int MinDist = INFINITY;
	
	for(V=0;VNv;V++){
		
		if(collected[V]==false && dist[V] 

 然而,很多时候会有多条最短路径

问题进阶:当有多条最短路径时,在这几条路径中挑选一条符合其他要求的路径;

此类问题往往有以下4中情况:

1.增加一个边权;即最短路径下,此边权最优的路径

2.增加一个点权;即最短路径下,经过的各个顶点点权和最优

3.直接问:有几条最短路;

4.终极问题:输出每条最短路径的具体路径;

对于前3个问题,我们往往只需增加一个对应的数组,就可以处理

案例参考:增加了一个边权 MOOC 数据结构 07-图6 旅游规划_鸿雁丨红豆灬的博客-CSDN博客

                  增加了点权和问最短路条数 MOOC 数据结构 1003 Emergency_鸿雁丨红豆灬的博客-CSDN博客

 现在我们来重点讨论下第4种情况,要求输出每条最短路具体路径,这也是前3个问题的通用解决方法:知道了每条最短路,再根据其他权重分析每条最短路的优劣势,从而选择最优的路径;

思考:

        1.要输出具体的路径,就需要保存每个顶点的每个前驱顶点,再从终点反推到起点;

        2.所以需要数量等于顶点个数的数组Pre[ ],记录每个顶点的每个前驱顶点;这些数组有的只保存一个值,即一个前驱顶点,有的保存几个;所以每个数组还需要一个值Size,来记录每个数组的元素个数;

        3.用全局变量二维数组 Vertex Pre[MaxVertexNum][MaxVertexNum],数组的第V行记录顶点V的前驱顶点;同时还需要一个全局的一维数组int Size[MaxVertexNum],Size[V]记录顶点V的前驱顶点个数;

                显然这不是一个很好的选择,最短路径下大部分顶点往往少数的前驱顶点,这就会造成极大的空间浪费;

        4.那就用局部变量根据实际的顶点个数 开辟二维数组Vertex Pre[Graph->Nv][Graph->Nv];但是传二维数组给函数时需要明确列数,好像有点麻烦,我暂时还不知道怎么传二维数组给函数;

        5.那就只能把二维数组转变为一维,方便传入函数;如何将二维数组转为一维,这是我自己想的方案:那就是把数组设计进结构体,然后在局部申请一个结构体数组;可能等到后面学的更深入的时候会有更好的解决办法,到时候再分享。

结构体:

typedef struct _Path{
	Vertex* Pre; //保存每个顶点的前驱顶点的数组
	int Size; //每个数组的元素个数
}Path;

局部变量结构体数组

Path path[Graph->Nv];

//初始化
Vertex V;
for(V=0;VNv;V++){
		
		path[V].Pre = malloc(Graph->Nv*sizeof(Vertex));
		path[V].Size = 0;
}

         6.设计好保存数据的结构,那就传入Dijkstra中,每次顶点W有更短路径前驱结点V时,if(dist[V]+Graph->G[V][W] < dist[W]),需要更新W的路径——清除前面的保存的路径,保存更短的路径V,UpdatePre( path,W,V );

void UpdatePre( Path path[],Vertex W,Vertex V )
{
	path[W].Size = 0;
	path[W].Pre[0] = V;
	path[W].Size++;
}

当有另一条最短路时,if(dist[V]+Graph->G[V][W] == dist[W]),增加前驱结点,AddPre( path,W,V );

void AddPre( Path path[],Vertex W,Vertex V )
{
	path[W].Pre[path[W].Size] = V;
	path[W].Size++;
}

这样通过改良Dijkstra算法,就可以得出各个顶点的前驱顶点path[ ]

bool Dijkstra( MGraph* Graph,int dist[],Path path[],Vertex S )
{
	int collected[MaxVertexNum];
	Vertex V,W;
	
	for(V=0;VNv;V++){
		
		dist[V] = Graph->G[S][V];
		if(dist[V]Nv;W++){ 
			
			if(collected[W])==false &&Graph->G[V][W]G[V][W] < 0){ 
					return false;       
				}
				
				if(dist[V]+Graph->G[V][W] < dist[W]){
					
					dist[W] = dist[V] + Graph->G[V][W]; 
					UpdatePre( path,W,V );     //********改动点2;
				}else if(dist[V]+Graph->G[V][W] == dist[W]){
                       
                    AddPre( path,W,V );    //*********带动点3;
                }
			}
		}
	} 
	
	return true; 
}

知道了各个顶点的前驱顶点,就可以设计递归函数,求解具体问题:当每次递归到起点时就是一条路径

终极问题:请输出每条最短路的具体路径

需要一个数组,记录每条路径的各个顶点:

Vertex SpecificShortestPath[Graph->Nv];
void DFS( Vertex Start,Vertex X,Path path[],Vertex SpecificShortestPath[],int Len );

Start 是起点,当递归到起点时,返回;

X是递归过程中的各个顶点;

path[ ]是保存每个顶点的前驱顶点的数组

SpecificShortestPath[ ] 保存具体的路径

Len 是 SpecificShortestPath[ ] 的长度

初次调用时:(Destination是终点),Len 取 0;

DFS( Start,Destination,path,SpecificShortestPath,0 );

 具体的DFS:

void DFS( Vertex Start,Vertex X,Path path[],Vertex SpecificShortestPath[],int Len )
{
	SpecificShortestPath[Len] = X;
	
	if(X==Start){
		
		PrintShortesPath( SpecificShortestPath,Len );
		return;
	}
	
	int i;
	for(i=0;i 

输出具体路径函数:

void PrintShortesPath( Vertex SpecificShortestPath[],int Len )
{
	int i;
	for(i=Len;i>=0;i--){
		
		printf("%d ",SpecificShortestPath[i]);
	}
	
	printf("n");	
}

其他问题,比如最短路的条数,考虑点权,考虑另一条边权,修改DFS都可以求解;

 暂时只能想到这种程度了,等后面有更好的思路时再来讨论,诸君共勉。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/879713.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号