第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
为空。
实现狄克斯特拉算法
-
设置一个数组
dist[0..n-1]
,dist[i]
用来保存从源点v0
到顶点i
的目前最短路径长度。 -
path[j]
保存源点到顶点j
的最短路径,实际上为最短路径上的前一个顶点u
,即path[j]=u
。 -
当求出最短路径后由
path[j]
向前推出源点到顶点j
的最短路径。
举例,有如下有向图,求从0
到其余顶点的最短路径:
下表给出了上述有向网G中从源点0
到其余各顶点的最短路径的求解过程。
最后求出顶点0
到1~6
各顶点的最短距离分别为4、5、6、10、9
和16
。
以求顶点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
。
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");
}
狄克斯特拉算法的时间复杂度为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关:求图(邻接表存储)最短路径的狄克斯特拉算法
任务描述
本关任务:图的存储结构为邻接表,要求编写函数实现狄克斯特拉算法。
相关知识
不论图的存储结构为邻接矩阵还是邻接表,狄克斯特拉算法思想是一致的,只是求图的最短路径过程中具体操作不同,例如:用邻接矩阵存储图时,可以直接读取边的权值,用邻接表存储图时,需要定义一个函数读取边的权值。
#define INFINITY 4270000 // 用整型最大值代替∞
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;
}
举例,有如下有向图,求从0
到其余顶点的最短路径:
下表给出了上述有向网G中从源点0
到其余各顶点的最短路径的求解过程。
最后求出顶点0
到1~6
各顶点的最短距离分别为4、5、6、10、9
和16
。
编程要求
网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;
}