实习三 求最短路径.docx
《实习三 求最短路径.docx》由会员分享,可在线阅读,更多相关《实习三 求最短路径.docx(12页珍藏版)》请在冰豆网上搜索。
实习三求最短路径
实习三
1.需求分析:
【问题描述】:
设计一个算法,求图中一个源点到其他各顶点的最短路径。
【基本要求】:
(1)以邻接表作为存储结构。
(2)用Dijkstra算法求最短路径。
(3)按长度非递减次序打印输出最短路径的长度及相应路径。
【开发环境】:
系统:
windows7
编程软件:
VC++6.0
2.设计:
邻接表是一种链式存储结构。
在邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对有向图是以顶点Vi为尾的弧)。
每个结点由3个域组成,其中邻接点域(dest)指示与顶点Vi邻接的点在图中的位置,链域(next)指示下一条边或弧的结点;数据域(weight)存储和边或弧相关的信息,如权值等。
所以一开始必须先定义邻接表的边结点类型以及邻接表类型,并对邻接表进行初始化,然后根据所输入的相关信息,包括图的顶点数、边数、是否为有向,以及各条边的起点与终点序号,建立图的邻接表。
图的邻接表建立后,利用狄克斯特拉算法求出最短路径。
狄克斯特拉算法的思想:
设置两个顶点的集合S和T,集合S中存放已找到最短路径的顶点,集合T中存放当前还未找到最短路径的顶点。
初始状态时,集合S中只包含源点,设为
,然后从集合T中选择到源点
路径长度最短的顶点u加入到集合S中,集合S中每加入一个新的顶点u,都要修改源点
到集合T中剩余顶点的当前最短路径长度值,集合T中各顶点的新的当前最短路径长度值为原来的当前最短路径长度值与从源点过顶点u到达该顶点的路径长度中的较小者。
此过程不断重复,直到集合T中的顶点全部加入到集合S中为止。
具体思想
如下所示的有向图
跟据如上图所示的有向图,建立如下图所示的邻接表存储结构,数组的data域存储图的顶点信息,source域存储该顶点在数组下标,这个下标也是所有以该顶点为狐尾的边在数组中的下标,adj域为该顶点的邻接顶点单链表的头指针。
其中结点结构体定义如下:
typedefstructNode{
intdest;//连接表的弧头顶点序号
intweight;
structNode*next;//单链表的下一个结点指针
}Edge;//邻接边单链表的结点结构体
typedefstruct{
DataTypedata;//顶点数据元素
intsource;//邻接边的狐尾顶点序号
Edge*adj;//邻接边的头指针
}AdjLHeight;
typedefstruct{
AdjLHeighta[MaxVertices];//邻接表数组
intnumOfVerts;//顶点个数
intnumOfEdges;//边个数
}AdjLGraph;//连接表结构体
typedefstruct{
introw;//行下标
intcol;//列下标
intweight;//权值
}RowCol;//边信息结构体
用邻接表构建图完成后,Dijkstra算法求最短路径如下:
voidDijkstra(AdjLGraph*G,intv0,intdistance[],intpath[])
{//带权图G从下标v0顶点到其他顶点的最短距离distance和最短路径下标path
intn=G->numOfVerts;//顶点个数
int*s=(int*)malloc(sizeof(int)*n);
intminDis,i,j,u;
Edge*p,*q;
p=G->a[v0].adj;
while(p!
=NULL)
{//顶点能一次到达的,路径长度为其权值
i=p->dest;
distance[i]=p->weight;
path[i]=v0;
p=p->next;
}
for(i=0;i{//顶点一次不能到达的,路径长度为MaxWeight
s[i]=0;
if(path[i]!
=v0)
{
path[i]=-1;
distance[i]=MaxWeight;
}
}
s[v0]=1;//将序号v0包含在集合s内
distance[v0]=0;//顶点自己到自己的路径为0
for(i=0;i{
minDis=MaxWeight;
for(j=0;jif(s[j]==0&&distance[j]{
u=j;
minDis=distance[j];
}
if(minDis==MaxWeight)return;
s[u]=1;//将选出的终点序号u并入S集合中
q=G->a[u].adj;//从第a[u]个点开始
while(q!
=NULL)
{
if(s[q->dest]==0&&q->weightweightdest])
{//若顶点通过几次到该点的和的权值小于一次到该点的权值,则修改其到该点的权值
distance[q->dest]=distance[u]+q->weight;
path[q->dest]=u;
}
q=q->next;
}
}
}
3.调试分析
首先以邻接表作为图的存储结构,其中邻接表创建图函数课本上已经给出,实现起来比较简单,重要的是Dijkastra算法求最短路径。
其时间复杂度为T(
)。
首先用充分了解Dijkastra算法思想,其中距离标号uj:
记录的是从起点到该节点的最短路长度的上界。
前趋标号pred(j):
当取到uj时,节点j前面的那个直接前趋(标号)。
算法通过不断改变这些标号进行秩代计算。
算法结束时,距离标号表示起点到节点的最短路长度。
前趋标号记录着最短路路径。
调试过程中顶点到其本身为所设定的最大值而不是零,调试发现设置一个distance[v0]=0,便可以解决该问题。
开始时v0在集合S中,然后在所有的不在S集合中的顶点之中,选取distance[i]为最小的一个,设为u;将选出的终点序号u并入S集合中。
4.用户手册
在VC++6.0中运行该程序后,便可通过Dijkstra算法求出顶点(本程序选取的顶点为1)到其他点的最短路径。
5.测试结果
6.源代码
………………………………………………………Dijkastra.h………………………………..
typedefstructNode{
intdest;//连接表的弧头顶点序号
intweight;
structNode*next;//单链表的下一个结点指针
}Edge;//邻接边单链表的结点结构体
typedefstruct{
DataTypedata;//顶点数据元素
intsource;//邻接边的狐尾顶点序号
Edge*adj;//邻接边的头指针
}AdjLHeight;
typedefstruct{
AdjLHeighta[MaxVertices];//邻接表数组
intnumOfVerts;//顶点个数
intnumOfEdges;//边个数
}AdjLGraph;//连接表结构体
typedefstruct{
introw;//行下标
intcol;//列下标
intweight;//权值
}RowCol;//边信息结构体
voidAdjInitiate(AdjLGraph*G)
{//初始化图G
inti;
G->numOfEdges=0;
G->numOfVerts=0;
for(i=0;iG->a[i].source=i;//置邻接边的弧头顶点序号
G->a[i].adj=NULL;
}
}
voidInsertVertex(AdjLGraph*G,inti,DataTypevertex)
{//在图G中的第i(0<=iif(i>=0&&iG->a[i].data=vertex;//存储顶点数据元素vertex
G->numOfVerts++;//个数加1
}
elseprintf("顶点越界");
}
voidInsertEdge(AdjLGraph*G,intv1,intv2,intweight)
{//在图G中加入边
Edge*p;
if(v1<0||v1>=G->numOfVerts||v2<0||v2>=G->numOfVerts){
printf("参数v1或v2越界出错!
");
return;
}
p=(Edge*)malloc(sizeof(Edge));//申请邻接边单链表结点空间
p->dest=v2;//置邻接边弧头序号
p->weight=weight;
p->next=G->a[v1].adj;//新结点插入单链表的表头
G->a[v1].adj=p;//头指针指向新的单链表表头
G->numOfEdges++;//边个数加1
}
voidDeleteEdge(AdjLGraph*G,intv1,intv2)
{//删除图G中的边
Edge*curr,*pre;
if(v1<0||v1>=G->numOfVerts||v2<0||v2>=G->numOfVerts){
printf("参数v1或v2越界出错!
");
return;
}
pre=NULL;
curr=G->a[v1].adj;
while(curr!
=NULL&&curr->dest!
=v2)
{//在v1顶点的邻接边单链表中查找v2顶点
pre=curr;
curr=curr->next;
}//删除邻接边
if(curr!
=NULL&&curr->dest==v2&&pre==NULL)
{//当邻接边的结点是单链表的第一个结点时
G->a[v1].adj=curr->next;
free(curr);
G->numOfEdges--;
}
elseif(curr!
=NULL&&curr->dest==v2&&pre!
=NULL)
{//当邻接边的结点不是单链表的第一个结点时
pre->next=curr->next;
free(curr);
G->numOfEdges--;
}
elseprintf("边不存在!
");//当邻接边不存在时
}
voidAdjDestroy(AdjLGraph*G)
{//撤销图G中的所有单链表占用的存储空间
inti;
Edge*p,*q;
for(i=0;inumOfVerts;i++){
p=G->a[i].adj;
while(p!
=NULL){
q=p->next;
free(p);
p=q;
}
}
}
voidCreatGraph(AdjLGraph*G,DataTypev[],intn,RowCold[],inte)
{//创建有n个顶点e条边的图G
//顶点信息存放在数组v中,边信息存放在数组d中
inti,k;
AdjInitiate(G);//初始化
for(i=0;ifor(k=0;kInsertEdge(G,d[k].row,d[k].col,d[k].weight);//插入边
}
voidDijkstra(AdjLGraph*G,intv0,intdistance[],intpath[])
{//带权图G从下标v0顶点到其他顶点的最短距离distance和最短路径下标path
intn=G->numOfVerts;//顶点个数
int*s=(int*)malloc(sizeof(int)*n);
intminDis,i,j,u;
Edge*p,*q;
p=G->a[v0].adj;
while(p!
=NULL)
{//顶点能一次到达的,路径长度为其权值
i=p->dest;
distance[i]=p->weight;
path[i]=v0;
p=p->next;
}
for(i=0;i{//顶点一次不能到达的,路径长度为MaxWeight
s[i]=0;
if(path[i]!
=v0)
{
path[i]=-1;
distance[i]=MaxWeight;
}
}
s[v0]=1;//将序号v0包含在集合s内
distance[v0]=0;//顶点自己到自己的路径为0
for(i=0;i{
minDis=MaxWeight;
for(j=0;jif(s[j]==0&&distance[j]{
u=j;
minDis=distance[j];
}
if(minDis==MaxWeight)return;
s[u]=1;//将选出的终点序号u并入S集合中
q=G->a[u].adj;//从第a[u]个点开始
while(q!
=NULL)
{
if(s[q->dest]==0&&q->weightweightdest])
{//若顶点通过几次到该点的和的权值小于一次到该点的权值,则修改其到该点的权值
distance[q->dest]=distance[u]+q->weight;
path[q->dest]=u;
}
q=q->next;
}
}
}
………………………………………………………main.cpp……………………………………
#include
#defineMaxVertices10
#defineMaxWeight10000
#include
typedefintDataType;
#include"Dijkastra.h"
voidmain(){
AdjLGraphG;
DataTypea[]={1,2,3,4,5,6};
RowCold[]={{0,1,20},{0,2,15},{1,0,2},{1,4,10},{1,5,30},{2,1,4},{2,5,10},{4,3,15},{5,3,4},{5,4,10}};
intdistance[6],path[6];
inti,n=6,e=10;
CreatGraph(&G,a,n,d,e);
Dijkstra(&G,0,distance,path);
printf("用Dijkstra算法求得从顶点%d到其他各顶点的最短距离为:
\n",G.a[0].data);
for(i=0;iprintf("到顶点%d的最短距离为%d\n",G.a[i].data,distance[i]);
}