实验六:图的单源最短路径算法

第1关:求图(邻接矩阵存储)最短路径的狄克斯特拉算法


任务描述

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

相关知识

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

求单源最短路径算法是由狄克斯特拉(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 min,num,dist[MAX_VERTEX_NUM],path[MAX_VERTEX_NUM],s[MAX_VERTEX_NUM];
    for(int i=0;i<g.vexnum;i++)
    {
        dist[i]=g.arcs[v][i].adj;
        if(dist[i]!=INFINITY)
        {
            path[i]=v;
        }else{
            path[i]=-1;
        }
    }
    for(int i=0;i<g.vexnum;i++)
    {
        s[i]=0;
    }
    s[v]=1;
    num=1;
    while(num<g.vexnum)
    {
        int mini=INFINITY,m;
        for(int i=0;i<g.vexnum;i++)
        {
            if(s[i]==0&&dist[i]<mini)
            {
                mini=dist[i];
                m=i;
            }
        }
        s[m]=1;
        Dispdistpath(dist,path,g.vexnum);
        for(int i=0;i<g.vexnum;i++)
        {
            if(s[i]==0&&(dist[i]>dist[m]+g.arcs[m][i].adj))
        {
dist[i]=dist[m]+g.arcs[m][i].adj;
path[i]=m;
        }
        }
        num++;
    }
    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");
}

第2关:求图(邻接表存储)最短路径的狄克斯特拉算法


任务描述

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

相关知识

不论图的存储结构为邻接矩阵还是邻接表,狄克斯特拉算法思想是一致的,只是求图的最短路径过程中具体操作不同,例如:用邻接矩阵存储图时,可以直接读取边的权值,用邻接表存储图时,需要定义一个函数读取边的权值。

 
  1. #define INFINITY 4270000 // 用整型最大值代替∞
  2. int GetWeight(ALGraph g,VertexType a,VertexType b ) //获取权值
  3. { int pa,pb;
  4. pa=LocateVex(g,a);
  5. pb=LocateVex(g,b);
  6. ArcNode* p;
  7. p=g.vertices[pa].firstarc;
  8. if(pa == pb)
  9. return 0;
  10. while(p!=NULL)
  11. { if( p->data.adjvex == pb)
  12. return p->data.info;
  13. else
  14. p=p->nextarc;
  15. }
  16. return INFINITY;
  17. }

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

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

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

最短路径数组

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

编程要求

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

  • void Dijkstra(ALGraph 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个顶点:

0 1 2 3 4 5 6

12条弧(边):

0→3 :6 0→2 :6 0→1 :4
1→4 :7 1→2 :1

2→5 :4 2→4 :6
3→5 :5 3→2 :2
4→6 :6
5→6 :8 5→4 :1
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;

#define INFINITY 4270000 // 用整型最大值代替∞ 
#include"ALGraph.h"

int GetWeight(ALGraph G,VertexType a,VertexType b );//获取权值

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

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

void Dijkstra(ALGraph g,int v)		
{
    //求从v到其他顶点的最短路径
    /********** Begin **********/
      int min,num,dist[MAX_VERTEX_NUM],path[MAX_VERTEX_NUM],s[MAX_VERTEX_NUM];
    for(int i=0;i<g.vexnum;i++)
    {
        dist[i]=GetWeight(g,g.vertices[v].data,g.vertices[i].data);
        if(dist[i]!=INFINITY)
        {
            path[i]=v;
        }else{
            path[i]=-1;
        }
    }
    for(int i=0;i<g.vexnum;i++)
    {
        s[i]=0;
    }
    s[v]=1;
    num=1;
    while(num<g.vexnum)
    {
        int mini=INFINITY,m;
        for(int i=0;i<g.vexnum;i++)
        {
            if(s[i]==0&&dist[i]<mini)
            {
                mini=dist[i];
                m=i;

            }
        }
        s[m]=1;
        Dispdistpath(dist,path,g.vexnum);
        for(int i=0;i<g.vexnum;i++)
        {
            if(s[i]==0&&(dist[i]>dist[m]+GetWeight(g,g.vertices[m].data,g.vertices[i].data)))
            {
                dist[i]=dist[m]+GetWeight(g,g.vertices[m].data,g.vertices[i].data);
                path[i]=m;
            }
        }
        num++;
    }
    Dispdistpath(dist,path,g.vexnum);
    DispAllPath(g,dist,path,s,v);
        

    /********** End **********/
}

void DispAllPath(ALGraph &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.vertices  [v].data <<"到"<<g.vertices [i].data <<"最短路径长度为:"<<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.vertices  [ apath[d] ].data ;
				for (j=d-1;j>=0;j--)	//再输出其他顶点
					//printf("→%d",apath[j]);
					cout<<"→"<<g.vertices  [ apath[j] ].data ;
				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");
}

int GetWeight(ALGraph G,VertexType a,VertexType b )//获取权值 
{    int pa,pb;
    pa=LocateVex(G,a);
    pb=LocateVex(G,b);
    ArcNode* p;
    p=G.vertices[pa].firstarc;
    if(pa == pb)
        return 0;
    while(p!=NULL)
    {    if( p->data.adjvex  == pb)
        return p->data.info;
        else
        p=p->nextarc;
    }
    return INFINITY;
}

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galaxy*★

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值