您的当前位置:首页正文

最小生成树和最短路径数据结构实验

2021-02-19 来源:步旅网


六月 18

实验报告

2015

数据结构 第八次实验

姓名:陈斌 学号:E 专业:13计算机科学与技术

学号 E 专业 计算机科学与技术 姓名 陈 斌 实验日期 教师签字 成绩

实 验 报 告

【实验名称】 最小生成树和最短路径 【实验目的】

(1) 掌握最小生成树以及最短路径的相关概念; (2) 掌握Prim算法和Kruskal算法; (3) 掌握Dijkstra算法 【实验内容】

采用普里姆算法求最小生成树

(1) 编写一个算法,对于教材图(a)所示的无向带权图G采用普里姆算法输出从顶点

V1出发的最小生成树。图的存储结构自选。

(2) 对于上图,采用克鲁斯卡尔算法输出该图的最小生成树。(提示:a.先对边按

权值从小到大排序,得有序边集E;为所有顶点辅设一个数组Vset,标记各顶点所处的连通分量,初始时各不相同。b. 依次从E中取出一条边(i,j),检查顶点i和j是否属于同一连通分量,如是,则重取下一条边;否则,该边即为生成树的一条边,输出该边,同时将所有与j处于同一连通分量的顶点的Vset值都修改为与i的相同。c.重复b步直至输出n-1条边。)

源代码:

:

#include<> #include<>

#include<> dj=INFINITY; /* 网 */ }

printf(\"请输入%d条边的顶点1 顶点2 权值(用空格隔开): \\n\ for(k=0;k<;++k) {

scanf(\"%s%s%d%*c\吃掉回车符 */ i=LocateVex(G,va); j=LocateVex(G,vb);

[i][j].adj=[j][i].adj=w; /* 无向 */ } =AN; return OK; }

typedef struct

{ /* 记录从顶点集U到V-U的代价最小的边的辅助数组定义 */ VertexType adjvex; VRType lowcost; }minside[MAX_VERTEX_NUM];

int minimum(minside SZ,MGraph G) { /* 求的最小正值 */ int i=0,j,k,min; while(!SZ[i].lowcost) i++;

min=SZ[i].lowcost; /* 第一个不为0的值 */ k=i;

for(j=i+1;j<;j++) if(SZ[j].lowcost>0)

if(min>SZ[j].lowcost) {

min=SZ[j].lowcost; k=j; } return k; }

void MiniSpanTree_PRIM(MGraph G,VertexType u)

{ /* 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边 算法 */

int i,j,k; minside closedge; k=LocateVex(G,u);

for(j=0;j<;++j) /* 辅助数组初始化 */ {

if(j!=k) {

strcpy(closedge[j].adjvex,u); closedge[j].lowcost=[k][j].adj; } }

closedge[k].lowcost=0; /* 初始,U={u} */ printf(\"最小代价生成树的各条边为:\\n\"); for(i=1;i<;++i) { /* 选择其余个顶点 */

k=minimum(closedge,G); /* 求出T的下一个结点:第K顶点 */

printf(\"(%s-%s)\\n\输出生成树的边 */ closedge[k].lowcost=0; /* 第K顶点并入U集 */ for(j=0;j<;++j)

if[k][j].adjtypedef struct node {

int va; E[k].va=i; E[k].vb=j; E[k].w=[i][j].adj; k++; } } }

Heapsort(E,G); Initialize(G);

a]; int sn2=Vset[E[j].vb]; a],[E[j].vb],E[j].w);

k++;

for (i=0;i<;i++) if (Vset[i]==sn2) Vset[i]=sn1; } j++; } }

void main() { MGraph G; CreateAN(G);

cout<<\"--------普里姆算法输出从顶点V1出发的最小生成树--------\\n\"<cout<<\"------------------------------------------------------\\n\"<cout<<\"------------------------------------------------------\"<运行结果:

采用迪杰斯特拉算法求单源最短路径

编写一个算法,采用迪杰斯特拉算法,输出如下图所示的有向带权图G中从顶点a到其他各顶点的最短路径长度和最短路径。图的存储结构自选。 b 4 e 15 6

源代码:

:

#include<> #include<>

#include<> exnum,&(*G).arcnum);

printf(\"请输入%d个顶点的值(<%d个字符):\\n\ for(i=0;i<(*G).vexnum;++i) /* 构造顶点向量 */ scanf(\"%s\

for(i=0;i<(*G).vexnum;++i) /* 初始化邻接矩阵 */ for(j=0;j<(*G).vexnum;++j) {

(*G).arcs[i][j].adj=INFINITY; /* 网 */ }

printf(\"请输入%d条弧的弧尾 弧头 权值(以空格作为间隔): \\n\

for(k=0;k<(*G).arcnum;++k) {

scanf(\"%s%s%d%*c\吃掉回车符 */ i=LocateVex(*G,va); j=LocateVex(*G,vb);

(*G).arcs[i][j].adj=w; /* 有向网 */ }

(*G).kind=DN; return OK; }

typedef int QElemType;

/*单链队列--队列的链式存储结构 */ typedef struct QNode {

QElemType data; struct QNode *next; }QNode,*QueuePtr; typedef struct {

QueuePtr front,rear; /* 队头、队尾指针 */ }LinkQueue; LinkQueue Q;

Status InitQueue(LinkQueue *Q) { /* 构造一个空队列Q */

(*Q).front=(*Q).rear=(QueuePtr)malloc(sizeof(QNode)); if(!(*Q).front) exit(OVERFLOW); (*Q).front->next=NULL; return OK; }

Status QueueEmpty(LinkQueue Q)

{ /* 若Q为空队列,则返回TRUE,否则返回FALSE */ if==

return TRUE; else

return FALSE; }

Status EnQueue(LinkQueue *Q,QElemType e) { /* 插入元素e为Q的新的队尾元素 */

QueuePtr p=(QueuePtr)malloc(sizeof(QNode)); if(!p) /* 存储分配失败 */ exit(OVERFLOW); p->data=e; p->next=NULL; (*Q).rear->next=p; (*Q).rear=p; return OK; }

Status DeQueue(LinkQueue *Q,QElemType *e)

{ /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */ QueuePtr p;

if((*Q).front==(*Q).rear) return ERROR; p=(*Q).front->next; *e=p->data;

(*Q).front->next=p->next; if((*Q).rear==p) (*Q).rear=(*Q).front; free(p); return OK; }

void ShortestPath_DIJ(MGraph G,int v0,PathMatrix *P,ShortPathTable *D) { /* 用Dijkstra算法求有向网G的v0顶点到其余顶点v的最短路径P[v]及带权长度 */

/* D[v]。若P[v][w]为TRUE,则w是从v0到v当前求得最短路径上的顶点。 */ /* final[v]为TRUE当且仅当v∈S,即已经求得从v0到v的最短路径 算法 */ int v,w,i,j,min;

Status final[MAX_VERTEX_NUM]; for(v=0;v<;++v) {

final[v]=FALSE; (*D)[v]=[v0][v].adj; for(w=0;w<;++w)

(*P)[v][w]=FALSE; /* 设空路径 */

if((*D)[v](*P)[v][v0]=TRUE; (*P)[v][v]=TRUE; } }

(*D)[v0]=0;

final[v0]=TRUE; /* 初始化,v0顶点属于S集 */ for(i=1;i<;++i) /* 其余个顶点 */

{ /* 开始主循环,每次求得v0到某个v顶点的最短路径,并加v到S集 */ min=INFINITY; /* 当前所知离v0顶点的最近距离 */ for(w=0;w<;++w)

if(!final[w]) /* w顶点在V-S中 */

if((*D)[w]} /* w顶点离v0顶点更近 */

final[v]=TRUE; /* 离v0顶点最近的v加入S集 */ EnQueue(&Q,v);

for(w=0;w<;++w) /* 更新当前最短路径及距离 */ {

if(!final[w]&&min{ /* 修改D[w]和P[w],w∈V-S */ (*D)[w]=min+[v][w].adj;

for(j=0;j<;++j)

(*P)[w][j]=(*P)[v][j]; (*P)[w][w]=TRUE; } } } }

void main() {

InitQueue(&Q);

int i,j,e,v0=0; /* v0为源点 */ MGraph g; PathMatrix p; ShortPathTable d; CreateDN(&g);

ShortestPath_DIJ(g,v0,&p,&d);

printf(\"最短路径数组p[i][j]如下:\\n\"); for(i=0;i<;++i) {

for(j=0;j<;++j)

printf(\"%2d\ printf(\"\\n\"); }

printf(\"%s到各顶点的最短路径长度为:\\n\ for(i=1;i<;++i)

printf(\"%s-%s:%d\\n\

int t[6];//用来存放最短路径的终点的序号(来自队列Q) for(i=0;i<6;i++) DeQueue(&Q,&t[i]);

printf(\"%s到各顶点的最短路径为:\\n\ for(i=1;i<;++i) {

cout<<[0]<<\"-\"<<[i]<<\"的最短路径为:\"<<[0]<<' '; for(j=1;j<;j++)

if(p[i][t[j-1]]) {

cout<<[t[j-1]]<<' ';

}

cout<运行结果:

【小结或讨论】

(1) 通过本次实验,掌握了最小生成树以及最短路径的相关概念,并且会实现Prim

算法、Kruskal算法以及Dijkstra算法。

(2) 在实现Prim算法时需附设一个辅助数组closedge,以记录从U到V-U具有最

小代价的边。

(3) 在实现Kruskal算法时为所有顶点附设一个数组Vset,标记各顶点所处的连通

分量,还附设了一个结构体变量存放各边的起点、终点和边的权值;在对各边按权值大小进行排序时,采用的是堆排序,初始建的是大顶堆,最终排完序后就是按边权值从小到大的有序序列。排序过程中注意双亲结点和左右孩子结点的序号问题,因为序号是从0开始的,所以结点i的孩子结点是2*i+1。 (4) 在实现Dijkstra算法时附设了二维数组P记录从V0到各顶点的最短路径上包

含的顶点,用数组D记录各最短路径的长度,此外还附设了一个队列Q,将每次找到的最短路径的终点入队,最后输出最短路径时根据当时入队的顺序依次输出,这里队列可以用一维数组代替,用队列可以更好地体现算法的思想。 (5) 从时间复杂度上看,Prim算法的时间复杂度为O(n^2),与边数无关,适用于

求边稠密的网的最小生成树;Kruskal算法的时间复杂度为O(eloge)(e为网中边的数目),适用于边稀疏的网的最小生成树。Dijkstra算法的时间复杂度为O(n^2)。

因篇幅问题不能全部显示,请点此查看更多更全内容