实验 图的表示与遍历完整.docx
《实验 图的表示与遍历完整.docx》由会员分享,可在线阅读,更多相关《实验 图的表示与遍历完整.docx(39页珍藏版)》请在冰豆网上搜索。
实验图的表示与遍历完整
实验五图的表示与遍历
一、实验目的
1、掌握图的邻接矩阵和邻接表表示
2、掌握图的深度优先和广度优先搜索方法
3、理解图的应用方法
二、实验预习
说明以下概念
1、深度优先搜索遍历:
从根开始一个一个搜索
2、广度优先搜索遍历:
从根的邻接点出发依次访问
3、拓扑排序:
一个无指向的点开始排序
4、最小生成树:
最小权的生成树
5、最短路径:
路径权数最小
三、实验内容和要求
1、阅读并运行下面程序,根据输入写出运行结果。
#include
#defineN20
#defineTRUE1
#defineFALSE0
intvisited[N];
typedefstruct/*队列的定义*/
{
intdata[N];
intfront,rear;
}queue;
typedefstruct/*图的邻接矩阵*/
{
intvexnum,arcnum;
charvexs[N];
intarcs[N][N];
}
graph;
voidcreateGraph(graph*g);/*建立一个无向图的邻接矩阵*/
voiddfs(inti,graph*g);/*从第i个顶点出发深度优先搜索*/
voidtdfs(graph*g);/*深度优先搜索整个图*/
voidbfs(intk,graph*g);/*从第k个顶点广度优先搜索*/
voidtbfs(graph*g);/*广度优先搜索整个图*/
voidinit_visit();/*初始化访问标识数组*/
voidcreateGraph(graph*g)/*建立一个无向图的邻接矩阵*/
{inti,j;
charv;
g->vexnum=0;
g->arcnum=0;
i=0;
printf("输入顶点序列(以#结束):
\n");
while((v=getchar())!
='#')
{
g->vexs[i]=v;/*读入顶点信息*/
i++;
}
g->vexnum=i;/*顶点数目*/
for(i=0;ivexnum;i++)/*邻接矩阵初始化*/
for(j=0;jvexnum;j++)
g->arcs[i][j]=0;
printf("输入边的信息:
\n");
scanf("%d,%d",&i,&j);/*读入边i,j*/
while(i!
=-1)/*读入i,j为-1时结束*/
{
g->arcs[i][j]=1;
g->arcs[j][i]=1;
scanf("%d,%d",&i,&j);
}
}
voiddfs(inti,graph*g)/*从第i个顶点出发深度优先搜索*/
{
intj;
printf("%c",g->vexs[i]);
visited[i]=TRUE;
for(j=0;jvexnum;j++)
if((g->arcs[i][j]==1)&&(!
visited[j]))
dfs(j,g);
}
voidtdfs(graph*g)/*深度优先搜索整个图*/
{
inti;
printf("\n从顶点%C开始深度优先搜索序列:
",g->vexs[0]);
for(i=0;ivexnum;i++)
if(visited[i]!
=TRUE)
dfs(i,g);
}
voidbfs(intk,graph*g)/*从第k个顶点广度优先搜索*/
{
inti,j;
queueqlist,*q;
q=&qlist;
q->rear=0;
q->front=0;
printf("%c",g->vexs[k]);
visited[k]=TRUE;
q->data[q->rear]=k;
q->rear=(q->rear+1)%N;
while(q->rear!
=q->front)
{
i=q->data[q->front];
q->front=(q->front+1)%N;
for(j=0;jvexnum;j++)
if((g->arcs[i][j]==1)&&(!
visited[j]))
{
printf("%c",g->vexs[j]);
visited[j]=TRUE;
q->data[q->rear]=j;
q->rear=(q->rear+1)%N;
}
}
}
voidtbfs(graph*g)/*广度优先搜索整个图*/
{
inti;
printf("\n从顶点%C开始广度优先搜索序列:
",g->vexs[0]);
for(i=0;ivexnum;i++)
if(visited[i]!
=TRUE)
bfs(i,g);
}
voidinit_visit()/*初始化访问标识数组*/
{
inti;
for(i=0;ivisited[i]=FALSE;
}
intmain()
{
graphga;
inti,j;
createGraph(&ga);
printf("无向图的邻接矩阵:
\n");
for(i=0;i{
for(j=0;jprintf("%3d",ga.arcs[i][j]);
printf("\n");
}
init_visit();
tdfs(&ga);
init_visit();
tbfs(&ga);
return0;
}
▪根据右图的结构验证实验,输入:
ABCDEFGH#
0,1
0,2
0,5
1,3
1,4
2,5
2,6
3,7
4,7
-1,-1
▪运行结果:
2、阅读并运行下面程序,补充拓扑排序算法。
#include
#include
#defineN20
typedefstructedgenode{/*图的邻接表:
邻接链表结点*/
intadjvex;/*顶点序号*/
structedgenode*next;/*下一个结点的指针*/
}edgenode;
typedefstructvnode{/*图的邻接表:
邻接表*/
chardata;/*顶点信息*/
intind;/*顶点入度*/
structedgenode*link;/*指向邻接链表指针*/
}vnode;
voidcreateGraph_list(vnodeadjlist[],int*p);/*建立有向图的邻接表*/
voidtopSort(vnodeg[],intn);/*拓扑排序*/
voidcreateGraph_list(vnodeadjlist[],int*p){/*建立有向图的邻接表*/
inti,j,n,e;
charv;
edgenode*s;
i=0;n=0;e=0;
printf("输入顶点序列(以#结束):
\n");
while((v=getchar())!
='#')
{
adjlist[i].data=v;/*读入顶点信息*/
adjlist[i].link=NULL;
adjlist[i].ind=0;
i++;
}
n=i;
*p=n;
/*建立邻接链表*/
printf("\n请输入弧的信息(i=-1结束):
i,j:
\n");
scanf("%d,%d",&i,&j);
while(i!
=-1){
s=(structedgenode*)malloc(sizeof(edgenode));
s->adjvex=j;
s->next=adjlist[i].link;
adjlist[i].link=s;
adjlist[j].ind++;/*顶点j的入度加1*/
e++;
scanf("%d,%d",&i,&j);
}
printf("邻接表:
");
for(i=0;iprintf("\n%c,%d:
",adjlist[i].data,adjlist[i].ind);
s=adjlist[i].link;
while(s!
=NULL){
printf("->%d",s->adjvex);
s=s->next;
}
}
}
voidtopSort(vnodeg[],intn){/*拓扑排序*/
}
intmain(){
vnodeadjlist[N];
intn,*p;
p=&n;
createGraph_list(adjlist,p);
return0;
}
▪根据输入,输出有向图的拓扑排序序列。
并画出有向图。
输入:
ABCDEF#
0,1
1,2
2,3
4,1
4,5
-1,-1
▪运行结果:
3、阅读并运行下面程序。
#include
#defineN20
#defineTRUE1
#defineINF32766/*邻接矩阵中的无穷大元素*/
#defineINFIN32767/*比无穷大元素大的数*/
typedefstruct
{/*图的邻接矩阵*/
intvexnum,arcnum;
charvexs[N];
intarcs[N][N];
}
graph;
voidcreateGraph_w(graph*g,intflag);
voidprim(graph*g,intu);
voiddijkstra(graphg,intv);
voidshowprim();
voidshowdij();
/*建带权图的邻接矩阵,若flag为1则为无向图,flag为0为有向图*/
voidcreateGraph_w(graph*g,intflag)
{
inti,j,w;
charv;
g->vexnum=0;
g->arcnum=0;
i=0;
printf("输入顶点序列(以#结束):
\n");
while((v=getchar())!
='#')
{
g->vexs[i]=v;/*读入顶点信息*/
i++;
}
g->vexnum=i;
for(i=0;i<6;i++)/*邻接矩阵初始化*/
for(j=0;j<6;j++)
g->arcs[i][j]=INF;
printf("输入边的信息:
\n");
scanf("%d,%d,%d",&i,&j,&w);/*读入边(i,j,w)*/
while(i!
=-1)/*读入i为-1时结束*/
{
g->arcs[i][j]=w;
if(flag==1)
g->arcs[j][i]=w;
scanf("%d,%d,%d",&i,&j,&w);
}
}
voidprim(graph*g,intu)/*出发顶点u*/
{
intlowcost[N],closest[N],i,j,k,min;
for(i=0;ivexnum;i++)/*求其他顶点到出发顶点u的权*/
{
lowcost[i]=g->arcs[u][i];
closest[i]=u;
}
lowcost[u]=0;
for(i=1;ivexnum;i++)/*循环求最小生成树中的各条边*/
{min=INFIN;
for(j=0;jvexnum;j++)/*选择得到一条代价最小的边*/
if(lowcost[j]!
=0&&lowcost[j]{
min=lowcost[j];
k=j;
}
printf("(%c,%c)--%d\n",g->vexs[closest[k]],g->vexs[k],lowcost[k]);/*输出该边*/
lowcost[k]=0;/*顶点k纳入最小生成树*/
for(j=0;jvexnum;j++)/*求其他顶点到顶点k的权*/
if(g->arcs[k][j]!
=0&&g->arcs[k][j]{
lowcost[j]=g->arcs[k][j];
closest[j]=k;
}
}
}
voidprintPath(graphg,intstartVex,intEndVex)
{
intstack[N],top=0;/*堆栈*/
inti,k,j;
intflag[N];/*输出路径顶点标志*/
k=EndVex;
for(i=0;ij=startVex;
printf("%c",g.vexs[j]);
flag[j]=1;
stack[top++]=k;
while(top>0)/*找j到k的路径*/
{
for(i=0;i{
if(path[k][i]==1&&flag[i]==0)/*j到k的路径含有i顶点*/
{
if(g.arcs[j][i]!
=INF)/*j到i的路径含有中间顶点*/
{
printf("->%c(%d)",g.vexs[i],g.arcs[j][i]);
/*输出j到k的路径的顶点i*/
flag[i]=1;
j=i;
k=stack[--top];
break;
}
else
{
if(i!
=k)stack[top++]=i;/*break;*/
}
}
}
}
voiddijkstra(graphg,intv){/*dijkstra算法求单源最短路径*/
intpath[N][N],dist[N],s[N];
intmindis,i,j,u,k;
for(i=0;idist[i]=g.arcs[v][i];
s[i]=0;
for(j=0;jpath[i][j]=0;
if(dist[i]path[i][v]=1;
path[i][i]=1;
}
}
dist[v]=0;
s[v]=1;
for(i=0,u=1;imindis=INFIN;
for(j=0;jif(s[j]==0)
if(dist[j]u=j;
mindis=dist[j];
}
s[u]=1;
for(j=0;jif((s[j]==0)&&dist[u]+g.arcs[u][j]dist[j]=dist[u]+g.arcs[u][j];
for(k=0;kpath[j][k]=path[u][k];
path[j][j]=1;
}
}
printf("\n顶点%c->到各顶点的最短路径\n",g.vexs[v]);
for(i=0;iprintf("\n顶点%c->顶点%c:
",g.vexs[v],g.vexs[i]);
if(dist[i]==INF||dist[i]==0)
printf("无路径");
else{
printf("%d",dist[i]);
printf("经过顶点:
");
printPath(g,v,i);/*输出v到i的路径*/
}
}
}
voidshowprim()/*最小生成树prim算法演示*/
{
graphga;
createGraph_w(&ga,1);
prim(&ga,0);
}
voidshowdij(){/*dijstra算法演示*/
graphga;
createGraph_w(&ga,0);
dijkstra(ga,0);
}
intmain(){
showprim();/*prim算法演示*/
getchar();
showdij();/*dijstra算法演示*/
return0;
}
▪下面的输入分别验证prim算法和dijstra算法。
输入实例的第一部分为无向图,求其最小生成树;输入的第二部分为有向图,求其最短路径。
最小生成树最短路径
ABCDEF#
0,1,6
0,2,1
0,3,5
1,2,5
1,4,3
2,3,5
2,4,6
2,5,4
3,5,2
4,5,6
-1,-1,-1
ABCDEF#
0,2,10
0,5,100
0,4,30
1,2,5
2,3,50
3,4,20
3,5,10
4,3,20
4,5,60
-1,-1,-1
▪运行结果:
(并画出两个图)
最小生成树最短路径
四、实验小结
图的表示和变厉需要很强的逻辑性
五、教师评语
数据结构实验报告
姓名
学号
号
实验地点
数学楼
指导教师
实验
名称
队列的表示与实现
1、实验目的
了解和掌握队列的数据类型描述及其特点;
完成链队列的初始化、入队、出队、取对头元素、显示操作的实现;
掌握队列的链式存储表示与基本操作算法实现;
掌握队列在实际问题中的应用和基本编程技巧。
2、实验方法
队列的抽象数据类型定义:
ADTQueue{
//数据对象:
D={a[i]|a[i]∈ElemSet,i=1,2,...,n,n>=0}
//数据关系:
R1={|a[i-1],a[i]∈D,i=2,...,n}
//约定其中a[1]端为队列头,a[n]端为队列尾
//基本操作:
InitQueue(&Q)//操作结果:
构造一个空队列Q。
DestoryQueue(&Q)//初始条件:
队列Q已存在
//操作结果:
队列Q被销毁,不再存在
ClearQueue(&Q)//初始条件:
队列Q已存在
//操作结果:
将Q清为空队列
QueueEmpty(Q)//初始条件:
队列Q已存在
//操作结果:
若队列Q为空队列,则返回TRUE,否则FALSE
QueueLength(Q)//初始条件:
队列Q已存在
//操作结果:
返回Q的元素个数,即队列的长度
GetHead(Q,&e)//初始条件:
Q为非空队列
//操作结果:
用e返回Q的队头元素
EnQueue(&Q,e)//初始条件:
队列Q已存在
//操作结果:
插入元素e为Q的新的队尾元素
DeQueue(&Q,&e)//初始条件:
Q为非空队列
//操作结果:
删除Q的队头元素,并用e返回其值
QueueTraverse(Q,visit())//初始条件:
Q已存在且非空
//操作结果:
从队头到队尾,依次对Q的每个数据元素调用函数visit()。
一旦visit()失败,则操作失败。
}ADTQueue
与线性表类似,队列有两种存储表示链队列和循环队列。
//-----基本操作的函数原型说明-----
statusINitQueue(LinkQueue&Q)//构造一个空队列Q
StatusDestoryQueue(LinkQueue&Q)//销毁队列Q,Q不再存在
StatusClearQueue(LinkQueue&Q)//将Q清为空队列
StatusQueueEmpty(LinkQueueQ)
//若队列Q为空队列,则返回TRUE,否则返回FALSE
intQueueLength(LinkQueueQ)//返回Q的元素个数,即为队列的长度
StatusGetHead(LinkQueueQ,QElemType&e)
//若队列不空,则用e返回Q的队头元素,并返回OK;否则返回ERROR
StatusENQueue(LinkQueue&Q,QElemTypee)//插入元素e为Q的新的队尾元素
StatusDeQueue(LinkQueue&Q,QElemType&e)
//若队列不空,则删除Q的队头元素,用e返回其值,并返回OK;否则返回ERROR
statusQueueTraverse(linkQueueQ,visit())
//从队头到队尾依次对队列Q中的每个元素调用函数visit()。
一旦visit失败,则操作失败。
链队列:
//单链队列——队列的链式存储结构
typedefstructQNode{
QElemTypedata;
structQNode*next;
}QNode,*QueuePtr;
typedefstruct{
QueuePtrfront;//队头指针
QueuePtrrear;//队尾指针
}LinkQueue;
//-----单链队列的基本操作的算法描述------
statusINitQueue(LinkQueue&Q){//构造一个空队列Q
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
if(!
Q.front)exit(OVERFLOW);//存储分配失败
Q.front->next=NULL;
returnOK;}
StatusDestoryQueue(LinkQueue&Q){//销毁队列Q,Q不再存在
while(Q.front){
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;}
returnOK;}
StatusClearQueue(LinkQueue&Q)
//将Q清为空队列
StatusQueueEmpty(LinkQueueQ)
//若队列Q为空队列,则返回TRUE,否则返回FALSE
intQueueLength(LinkQueueQ)
//返回Q的元素个数,即为队列的长度
StatusGetHead(LinkQueueQ,QElemType&e)
//若队列不空,则用e返回Q的队头元素,并返回OK;否则返回ERROR
StatusENQueue(LinkQueue&Q,QElemTypee){//插入元素e为Q的新的队尾元素
p=(QueuePtr)malloc(sizeof(QNode));
if(!
p)exit(OVERFLOW);//存储分配失败
p->data=e;p->next=NULL;
Q.rear->next=p;
Q.rear=p;
re