数据结构—图的操作与应用—第9关:求图(邻接矩阵存储)最短路径的狄克斯特拉算法


任务描述

本关任务:图的存储结构为邻接矩阵,要求编写函数实现狄克斯特拉算法。

相关知识

求带权有向图最短路径问题分为两种情况:求从一个顶点到其他各顶点的最短路径,称之为单源最短路径问题;求每对顶点之间的最短路径,称之为多源最短路径问题。

求单源最短路径算法是由狄克斯特拉(Dijkstra)提出的,称为狄克斯特拉算法,是一个按路径长度递增的顺序逐步产生最短路径的方法。

狄克斯特拉算法思想

给定一个图G和一个起始顶点即源点v,求v到其他顶点的最短路径长度及最短路径。

① 初始时,顶点集S只包含源点,即S={v0​},顶点v0​到自已的距离为0。顶点集T包含除v0​外的其他顶点,源点v0​T中顶点i的距离为边上的权(若v0​i有边<v0​,i>)或(若顶点i不是v0​的出边相邻点)。

② 从T中选取一个顶点u,它是源点v0​U中距离最小的一个顶点,然后把顶点u加入S中(该选定的距离就是源点v0​到顶点u的最短路径长度)。

③ 以顶点u为新考虑的中间点,修改源点v0​U中各顶点j(j∈T)的距离。

重复步骤②和③直到S包含所有的顶点即T为空。

实现狄克斯特拉算法
  1. 设置一个数组dist[0..n-1]dist[i]用来保存从源点v0​到顶点i的目前最短路径长度。

  2. path[j]保存源点到顶点j的最短路径,实际上为最短路径上的前一个顶点u,即path[j]=u

  3. 当求出最短路径后由path[j]向前推出源点到顶点j的最短路径。

举例,有如下有向图,求从0到其余顶点的最短路径:

求有向图从0到其余顶点的最短路径

下表给出了上述有向网G中从源点0到其余各顶点的最短路径的求解过程。

最短路径数组

最后求出顶点01~6各顶点的最短距离分别为4、5、6、10、916

以求顶点0到顶点4的最短路径为例说明通过path求最短路径的过程: path[4]=5,path[5]=2,path[2]=1,path[1]=0(源点), 则顶点0到顶点4的最短路径逆向为4、5、2、1、0,则正向最短路径为0→1→2→5→4

 
  1. void DispAllPath(MGraph &g,int dist[],int path[],int S[],int v) //输出从顶点v出发的所有最短路径
  2. {
  3. int i,j,k,count=0;
  4. int apath[MAX_VERTEX_NUM],d; //存放一条最短路径(逆向)及其顶点个数
  5. for (i=0;i<g.vexnum;i++)
  6. if (path[i]!=-1)
  7. count++;
  8. if (count==1) //path中只有一个不为-1时表示没有路径
  9. { printf("从指定的顶点到其他顶点都没有路径!!!\n");
  10. return;
  11. }
  12. for (i=0;i<g.vexnum;i++) //循环输出从顶点v到i的路径
  13. if (S[i]==1 && i!=v)
  14. {
  15. //printf("从%s到%s最短路径长度为:%s\t路径:",g.vexs [v],g.vexs[i],dist[i]);
  16. cout<<"从"<<g.vexs [v]<<"到"<<g.vexs[i]<<"最短路径长度为:"<<dist[i]<<"\t";
  17. d=0; apath[d]=i; //添加路径上的终点
  18. k=path[i];
  19. if (k==-1) //没有路径的情况
  20. printf("无路径\n");
  21. else //存在路径时输出该路径
  22. { while (k!=v)
  23. { d++; apath[d]=k;
  24. k=path[k];
  25. }
  26. d++; apath[d]=v; //添加路径上的起点
  27. //printf("%d",apath[d]); //先输出起点
  28. cout<<g.vexs [ apath[d] ];
  29. for (j=d-1;j>=0;j--) //再输出其他顶点
  30. //printf("→%d",apath[j]);
  31. cout<<"→"<<g.vexs [ apath[j] ];
  32. printf("\n");
  33. }
  34. }
  35. }
  36. void Dispdistpath(int dist[],int path[],int n) //输出dist数组和path数组
  37. {
  38. int i;
  39. printf("dist:\t");
  40. for (i=0;i<n;i++)
  41. if (dist[i]==INFINITY)
  42. printf("%s\t","∞");
  43. else
  44. printf("%d\t",dist[i]);
  45. printf("\n");
  46. printf("path:\t");
  47. for (i=0;i<n;i++)
  48. printf("%d\t",path[i]);
  49. printf("\n");
  50. }

狄克斯特拉算法的时间复杂度为O(n2)

编程要求

网G的存储结构为邻接矩阵,编写函数利用狄克斯特拉(Dijkstra))求图的单源最短路径:

  • void Dijkstra(MGraph g,int v); //求从v到其他顶点的最短路径

测试说明

平台会对你编写的代码进行测试:

测试输入: 1 lt4.txt

输入说明: 第一行输入1,表示输入图的类型为有向网。 第二行输入文件名,该文件里保存了图的数据信息,内容如下: 7 12 0 1 2 3 4 5 6 0 1 4 0 2 6 0 3 6 1 2 1 1 4 7 2 4 6 2 5 4 3 2 2 3 5 5 4 6 6 5 4 1 5 6 8 第1行为图的顶点的个数n; 第2行为图的边的条数m; 第3行至第n+2行是n个顶点的数据; 第n+3行至第n+m+2行是m条边的数据;

预期输出: 有向网 7个顶点12条边。顶点依次是: 0 1 2 3 4 5 6 图的邻接矩阵: ∞ 4 6 6 ∞ ∞ ∞ ∞ ∞ 1 ∞ 7 ∞ ∞
∞ ∞ ∞ ∞ 6 4 ∞
∞ ∞ 2 ∞ ∞ 5 ∞
∞ ∞ ∞ ∞ ∞ ∞ 6
∞ ∞ ∞ ∞ 1 ∞ 8 ∞ ∞ ∞ ∞ ∞ ∞ ∞
dist: ∞ 4 6 6 ∞ ∞ ∞
path: -1 0 0 0 -1 -1 -1
dist: ∞ 4 5 6 11 ∞ ∞
path: -1 0 1 0 1 -1 -1
dist: ∞ 4 5 6 11 9 ∞
path: -1 0 1 0 1 2 -1
dist: ∞ 4 5 6 11 9 ∞
path: -1 0 1 0 1 2 -1
dist: ∞ 4 5 6 10 9 17 path: -1 0 1 0 5 2 5
dist: ∞ 4 5 6 10 9 16 path: -1 0 1 0 5 2 4 dist: ∞ 4 5 6 10 9 16 path: -1 0 1 0 5 2 4
从0到1最短路径长度为:4 0→1 从0到2最短路径长度为:5 0→1→2 从0到3最短路径长度为:6 0→3 从0到4最短路径长度为:10 0→1→2→5→4 从0到5最短路径长度为:9 0→1→2→5 从0到6最短路径长度为:16 0→1→2→5→4→6

输出说明: 第一行输出图的类型。 第二部分起输出图的顶点和边的数据信息。 第三部分输出辅助数组的变化过程。 第四部分输出从起点到其余各顶点的最短路径。

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h>
#include<limits.h>  
#include<iostream>
using namespace std;
#include"MGraph.h"


void Dijkstra(MGraph g,int v);		//求从v到其他顶点的最短路径
void DispAllPath(MGraph &g,int dist[],int path[],int S[],int v) ;//输出从顶点v出发的所有最短路径
void Dispdistpath(int dist[],int path[],int n);	//输出dist数组和path数组

int main()
{
	MGraph g;
	int i,j,n;
	CreateGraphF(g);           /* 利用数据文件创建有向图*/
	Display(g);	/* 输出有向图*/  
	Dijkstra(g,0);	
	return 0;
}

void Dijkstra(MGraph g,int v)		
{	
    //求从v到其他顶点的最短路径
    /********** Begin **********/
    int dist[MAX_VERTEX_NUM];
    int path[MAX_VERTEX_NUM];
    int S[MAX_VERTEX_NUM];
    int mindis,i,j,u=0;
    for (i=0;i<g.vexnum;i++)
    {
        dist[i]=g.arcs[v][i].adj;
        S[i]=0;
        if (g.arcs[v][i].adj < INFINITY)
            path[i]=v;
        else
            path[i]=-1;
    }
    Dispdistpath(dist,path,g.vexnum);
    S[v]=1;
    for (i=0;i<g.vexnum-1;i++)
    {
        mindis=INFINITY;
        for(j=0;j<g.vexnum;j++)
            if(S[j]==0&&dist[j]<mindis){
                u=j;
                mindis=dist[j];
            }
        S[u]=1;
        for(j=0;j<g.vexnum;j++)
            if(S[j]==0)
                if(g.arcs[u][j].adj < INFINITY &&dist[u]+g.arcs[u][j].adj<dist[j]){
                    dist[j]=dist[u]+g.arcs[u][j].adj;
                    path[j]=u;
                }
        Dispdistpath(dist,path,g.vexnum);
    }
    DispAllPath(g,dist,path,S,v);
    /********** End **********/
}

void DispAllPath(MGraph &g,int dist[],int path[],int S[],int v) //输出从顶点v出发的所有最短路径
{
	int i,j,k,count=0;
	int apath[MAX_VERTEX_NUM],d;				//存放一条最短路径(逆向)及其顶点个数
	for (i=0;i<g.vexnum;i++)
		if (path[i]!=-1)
			count++;
	if (count==1)						//path中只有一个不为-1时表示没有路径
	{	printf("从指定的顶点到其他顶点都没有路径!!!\n");
		return;
	}
	for (i=0;i<g.vexnum;i++)					//循环输出从顶点v到i的路径
		if (S[i]==1 && i!=v)
		{
			//printf("从%s到%s最短路径长度为:%s\t路径:",g.vexs [v],g.vexs[i],dist[i]);
			cout<<"从"<<g.vexs [v]<<"到"<<g.vexs[i]<<"最短路径长度为:"<<dist[i]<<"\t";
			d=0; 			apath[d]=i;			//添加路径上的终点
			k=path[i];
			if (k==-1)					//没有路径的情况
				printf("无路径\n");
			else						//存在路径时输出该路径
			{	while (k!=v)
				{	d++; apath[d]=k;
					k=path[k];
				}
				d++; apath[d]=v;		//添加路径上的起点
				//printf("%d",apath[d]);	//先输出起点
				cout<<g.vexs [ apath[d] ];
				for (j=d-1;j>=0;j--)	//再输出其他顶点
					//printf("→%d",apath[j]);
					cout<<"→"<<g.vexs [ apath[j] ];
				printf("\n");
			}
		}
}


void Dispdistpath(int dist[],int path[],int n)	//输出dist数组和path数组
{
	int i;
	printf("dist:\t");
	for (i=0;i<n;i++)
		if (dist[i]==INFINITY)
			printf("%s\t","~");
		else
			printf("%d\t",dist[i]);
	printf("\n");
	printf("path:\t");
	for (i=0;i<n;i++)
		printf("%d\t",path[i]);
	printf("\n");
}

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值