一、需求分析
1.问题描述:
很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示连通的无向图上行遍全部结点的操作。
2.基本要求:
以邻接多重表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列
和相应生成树的边集。
3.测试数据:
#include<stdio.h> #include<malloc.h> #define MAX 30 typedef struct ENode { int ivex, jvex; /*该边依附的两个顶点在数组中的序号*/ struct ENode *ilink; /*指向下一条依附于顶点ivex的边*/ struct ENode *jlink; /*指向下一条依附于顶点jvex的边*/ }ENode; typedef struct VNode { int mark; char data; //顶点信息 int number; //顶点编号 ENode *firstedge; }VNode; typedef struct { VNode amlist[MAX]; int numberOfVerts; int numberOfEerts; }Graph; //图的信息 typedef struct QENode { int data; struct QENode *next; }QENode; //队列结点 typedef struct { QENode *rear; QENode *front; }LinkQueue; //队列 void InitQueue(LinkQueue *Q) //队列的初始化 { Q->front =Q->rear =(QENode *)malloc(sizeof(QENode)); Q->front ->next =NULL; } void QueueAppend(LinkQueue *Q,int v)//入队列 { QENode *p; p=(QENode *)malloc(sizeof(QENode)); p->data =v; p->next =NULL; Q->rear ->next =p; Q->rear =p; } void QueueDelete(LinkQueue *Q,int *v)//出队列 { QENode *p; if(Q->front ==Q->rear ) return; p=Q->front ->next ; *v=p->data ; Q->front ->next =p->next ; if(Q->rear ==p) Q->rear =Q->front ; free(p); } void Initilized(Graph *graph)//图的初始化 { graph=(Graph *)malloc (sizeof(Graph)); graph->numberOfVerts =0; graph->numberOfEerts =0; } void CreateGraph(Graph *graph)//图的创建图 { ENode *p,*q,*e; int i; printf("请输入连通无向图的顶点数和边数 例如 3 3:\n"); scanf("%d %d",&graph->numberOfVerts,&graph->numberOfEerts); for(i=1;i<=graph->numberOfVerts;i++) { printf("请输入第%d个顶点的信息:\n",i); scanf("%s",&graph->amlist [i].data ); graph->amlist [i].number =i; graph->amlist[i].firstedge=NULL; graph->amlist [i].mark =0; } for(i=1;i<=graph->numberOfEerts;i++) { p=(ENode *)malloc(sizeof(ENode)); printf("请输入每条边的信息(编号小的在前 例如1 3回车1 2回车2 3)\n"); scanf("%d %d",&p->ivex,&p->jvex); p->ilink =p->jlink =NULL; if(graph->amlist [p->ivex ].firstedge==NULL ) graph->amlist [p->ivex ].firstedge =p; else { q=graph->amlist [p->ivex ].firstedge ; while(q!=NULL) { e=q; if(q->ivex ==p->ivex ) q=q->ilink ; else q=q->jlink ; } if(e->ivex ==p->ivex ) e->ilink =p; else e->jlink =p; } if(graph->amlist [p->jvex ].firstedge==NULL ) graph->amlist [p->jvex ].firstedge =p; else { q=graph->amlist [p->jvex ].firstedge ; while(q!=NULL) { e=q; if(q->ivex ==p->ivex ) q=q->ilink ; else q=q->jlink ; } if(e->ivex ==p->ivex ) e->ilink =p; else e->jlink =p; } } } void SetMark(Graph *graph)//设置访问标记 { int i; for(i=1;i<=graph->numberOfVerts ;i++) graph->amlist [i].mark =0; } void DFS(Graph *graph,int v)//深度遍历 { ENode *p; printf("%d ",v); graph->amlist [v].mark =1; p=graph->amlist [v].firstedge ; while(p!=NULL) { if(p->ivex ==v) { if(graph->amlist [p->jvex ].mark ==0) { printf("<%d,%d>\n",p->ivex ,p->jvex ); DFS(graph,p->jvex ); } p=p->ilink ; } else { if(graph->amlist [p->ivex].mark ==0) { printf("<%d,%d>\n",p->jvex ,p->ivex ); DFS(graph,p->ivex ); } p=p->jlink ; } } } void BFS(Graph *graph,int u)//广度遍历 { LinkQueue Q; ENode *p; InitQueue(&Q); printf("%d ",u); graph->amlist [u].mark =1; QueueAppend(&Q,u); while(Q.front !=Q.rear ) { QueueDelete(&Q,&u); p=graph->amlist [u].firstedge ; while(p!=NULL) { if(p->ivex ==u) { if(graph->amlist [p->jvex ].mark ==0) { QueueAppend(&Q,p->jvex ); graph->amlist [p->jvex ].mark =1; printf("<%d,%d>\n",p->ivex ,p->jvex ); printf("%d ",p->jvex ); } p=p->ilink ; } else { if(graph->amlist [p->ivex ].mark ==0) { QueueAppend(&Q,p->ivex ); graph->amlist [p->ivex ].mark =1; printf("<%d,%d>\n",p->jvex ,p->ivex ); printf("%d ",p->ivex ); } p=p->jlink ; } } } } int main() { int v1,v2; Graph graph; char order; Initilized(&graph); CreateGraph(&graph); printf("\n输入深度广度遍历的起始点:\n"); scanf("%d",&v2); v1=v2; printf("\n深度遍历序列及相应的生成树:\n顶点序列: 生成树边集:\n"); SetMark(&graph); DFS(&graph,v2); printf("\n广度遍历序列及相应生成树:\n顶点序列:生成树边集:\n"); SetMark(&graph); BFS(&graph,v1); return 0; }
实习四 图及其应用
题目: 图遍历的演示 实习时间:2012/11/20
一、需求分析
1.问题描述:
很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示连通的无向图上行遍全部结点的操作。
2.基本要求:
以邻接多重表为存储结构,实现连通无向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列
和相应生成树的边集。
3.测试数据:
二、设计
1. 设计思想
(1)存储结构
邻接多重表为存储结构
(2)主要算法基本思想
所有代码思想均由老师授课所得:
深度优先搜索(Depth-first Search,DFS)
① 首先访问起始顶点v,再访问图中与v相邻接的且未被
访问过的任一顶点w1;
② 再从w1出发,访问与w1相邻接的且未被访问过的任一
顶点w2;
③ 从w2出发,重复与步骤②类似的访问,直至遇到一个
所有邻接点均被访问过的顶点为止;
④ 沿刚才访问的次序,反向回到一个尚有邻接点未被访
问过的顶点,再从该顶点出发,重复与步骤③相类似
的访问,直到所有的被访问过的顶点的邻接顶点均被
访问过为止。
广度优先搜索(Breadth-first Search,BFS)
① 访问起始顶点v后,依次访问与v相邻接的所有顶点
w1, w2, …, wt;
② 再按w1, w2, …, wt的顺序,访问其中每一个顶点的
所有未被访问过的邻接顶点;对w1为:w11, w12, …,
w1m;…;对wt为:wt1, wt2, …, wtn等;
③ 再按w11, w12, …, w1m, w21, …, wt1, wt2, …, wtn的顺序,
去访问它们各自的未被访问过的邻接顶点。依次类
推,直到图中所有被访问过的顶点的邻接顶点都被
访问过为止。
2. 设计表示
(1)函数调用关系图
main→Initilized→CreateGraph→SetMark→DFS→BFS
(2)函数接口规格说明
void Initilized(Graph *graph) // graph指向的图的初始化
void CreateGraph(Graph *graph) //graph指向的图的创建图
void SetMark(Graph *graph) //设置graph指向的图的顶点访问标记
void DFS(Graph *graph,int v) //深度遍历graph指向的图的点amlist [v]
void BFS(Graph *graph,int u) //广度遍历graph指向的图的点amlist [v]
3. 实现注释 (即各项功能的实现程度)
程序缺乏健壮性。
4. 详细设计(主要函数)
【1】void Initilized(Graph *graph)//图的初始化
{
graph=(Graph *)malloc (sizeof(Graph));
graph->numberOfVerts =0;
graph->numberOfEerts =0;
}
【2】void CreateGraph(Graph *graph)//图的创建图
{
ENode *p,*q,*e;
int i;
printf("请输入连通无向图的顶点数和边数 例如 3 3:\n");
scanf("%d %d",&graph->numberOfVerts,&graph->numberOfEerts);
for(i=1;i<=graph->numberOfVerts;i++)
{
printf("请输入第%d个顶点的信息:\n",i);
scanf("%s",&graph->amlist [i].data );
graph->amlist [i].number =i;
graph->amlist[i].firstedge=NULL;
graph->amlist [i].mark =0;
}
for(i=1;i<=graph->numberOfEerts;i++)
{
p=(ENode *)malloc(sizeof(ENode));
printf("请输入每条边的信息(编号小的在前 例如1 3回车1 2回车2 3)\n");
scanf("%d %d",&p->ivex,&p->jvex);
p->ilink =p->jlink =NULL;
if(graph->amlist [p->ivex ].firstedge==NULL )
graph->amlist [p->ivex ].firstedge =p;
else
{
q=graph->amlist [p->ivex ].firstedge ;
while(q!=NULL)
{
e=q;
if(q->ivex ==p->ivex )
q=q->ilink ;
else
q=q->jlink ;
}
if(e->ivex ==p->ivex )
e->ilink =p;
else
e->jlink =p;
}
if(graph->amlist [p->jvex ].firstedge==NULL )
graph->amlist [p->jvex ].firstedge =p;
else
{
q=graph->amlist [p->jvex ].firstedge ;
while(q!=NULL)
{
e=q;
if(q->ivex ==p->ivex )
q=q->ilink ;
else
q=q->jlink ;
}
if(e->ivex ==p->ivex )
e->ilink =p;
else
e->jlink =p;
}
}
}
【3】void SetMark(Graph *graph)//设置访问标记
{
int i;
for(i=1;i<=graph->numberOfVerts ;i++)
graph->amlist [i].mark =0;
}
【4】void DFS(Graph *graph,int v)//深度遍历
{
ENode *p;
printf("%d ",v);
graph->amlist [v].mark =1;
p=graph->amlist [v].firstedge ;
while(p!=NULL)
{
if(p->ivex ==v)
{
if(graph->amlist [p->jvex ].mark ==0)
{
printf("<%d,%d>\n",p->ivex ,p->jvex );
DFS(graph,p->jvex );
}
p=p->ilink ;
}
else
{
if(graph->amlist [p->ivex].mark ==0)
{
printf("<%d,%d>\n",p->jvex ,p->ivex );
DFS(graph,p->ivex );
}
p=p->jlink ;
}
}
}
【5】void BFS(Graph *graph,int u)//广度遍历
{
LinkQueue Q;
ENode *p;
InitQueue(&Q);
printf("%d ",u);
graph->amlist [u].mark =1;
QueueAppend(&Q,u);
while(Q.front !=Q.rear )
{
QueueDelete(&Q,&u);
p=graph->amlist [u].firstedge ;
while(p!=NULL)
{
if(p->ivex ==u)
{
if(graph->amlist [p->jvex ].mark ==0)
{
QueueAppend(&Q,p->jvex );
graph->amlist [p->jvex ].mark =1;
printf("<%d,%d>\n",p->ivex ,p->jvex );
printf("%d ",p->jvex );
}
p=p->ilink ;
}
else
{
if(graph->amlist [p->ivex ].mark ==0)
{
QueueAppend(&Q,p->ivex );
graph->amlist [p->ivex ].mark =1;
printf("<%d,%d>\n",p->jvex ,p->ivex );
printf("%d ",p->ivex );
}
p=p->jlink ;
}
}
}
}
三、调试分析
1.调试过程中遇到的主要问题是如何解决的;
调试过程中存在少许C语言的基础语法错误,经独立仔细观察和调试修改正确,最大的难题是将图中的各类算法实践要运用上几章节的内容,实现时需不断的温习以前的知识,出现多次错误。终于在自己独立多次多天修改下,终于完成。
2.对设计和编码的回顾讨论和分析;
总的来说,这个程序写的相当困难,虽然老师已经把大体算法思路都提到过。但是,由于算法和语法的知识掌握的太不牢固,对好多以前学的数据结构还不是特别熟悉,需要不断尝试与温习。所以,今后应该多温习多练习才好。
3.时间和空间复杂度的分析;
【1】:广度遍历算法 时间O(n),空间O(1)
【2】:深度遍历算法 时间O(n), 空间O(1)
4.改进设想;
程序能实现预期功能,只是其中的改进空间可能较大
【1】:图的构造函数
【2】:广度遍历算法
5.经验和体会等。
多动手编程,才能熟练灵活的掌握C语言基础知识,才能更好的理解掌握数据结构的精髓。从而避免基础语法错误,让代码变得更简洁高效。如此才能准确高效的解决问题。在今后的编程过程中要更注重代码的熟练掌握,多的温习代码思想,多的动手编程。
四、用户手册(即使用说明)
仅需按照提示的例子输入即可。若出错,则重新来过。
五、运行结果
运行环境:C-free
测试数据:
六、源程序清单
#include<stdio.h>
#include<malloc.h>
#define MAX 30
typedef struct ENode
{
int ivex, jvex; /*该边依附的两个顶点在数组中的序号*/
struct ENode *ilink; /*指向下一条依附于顶点ivex的边*/
struct ENode *jlink; /*指向下一条依附于顶点jvex的边*/
}ENode;
typedef struct VNode
{
int mark;
char data; //顶点信息
int number; //顶点编号
ENode *firstedge;
}VNode;
typedef struct
{
VNode amlist[MAX];
int numberOfVerts;
int numberOfEerts;
}Graph; //图的信息
typedef struct QENode
{
int data;
struct QENode *next;
}QENode; //队列结点
typedef struct
{
QENode *rear;
QENode *front;
}LinkQueue; //队列
void InitQueue(LinkQueue *Q) //队列的初始化
{
Q->front =Q->rear =(QENode *)malloc(sizeof(QENode));
Q->front ->next =NULL;
}
void QueueAppend(LinkQueue *Q,int v)//入队列
{
QENode *p;
p=(QENode *)malloc(sizeof(QENode));
p->data =v;
p->next =NULL;
Q->rear ->next =p;
Q->rear =p;
}
void QueueDelete(LinkQueue *Q,int *v)//出队列
{
QENode *p;
if(Q->front ==Q->rear )
return;
p=Q->front ->next ;
*v=p->data ;
Q->front ->next =p->next ;
if(Q->rear ==p)
Q->rear =Q->front ;
free(p);
}
void Initilized(Graph *graph)//图的初始化
{
graph=(Graph *)malloc (sizeof(Graph));
graph->numberOfVerts =0;
graph->numberOfEerts =0;
}
void CreateGraph(Graph *graph)//图的创建图
{
ENode *p,*q,*e;
int i;
printf("请输入连通无向图的顶点数和边数 例如 3 3:\n");
scanf("%d %d",&graph->numberOfVerts,&graph->numberOfEerts);
for(i=1;i<=graph->numberOfVerts;i++)
{
printf("请输入第%d个顶点的信息:\n",i);
scanf("%s",&graph->amlist [i].data );
graph->amlist [i].number =i;
graph->amlist[i].firstedge=NULL;
graph->amlist [i].mark =0;
}
for(i=1;i<=graph->numberOfEerts;i++)
{
p=(ENode *)malloc(sizeof(ENode));
printf("请输入每条边的信息(编号小的在前 例如1 3回车1 2回车2 3)\n");
scanf("%d %d",&p->ivex,&p->jvex);
p->ilink =p->jlink =NULL;
if(graph->amlist [p->ivex ].firstedge==NULL )
graph->amlist [p->ivex ].firstedge =p;
else
{
q=graph->amlist [p->ivex ].firstedge ;
while(q!=NULL)
{
e=q;
if(q->ivex ==p->ivex )
q=q->ilink ;
else
q=q->jlink ;
}
if(e->ivex ==p->ivex )
e->ilink =p;
else
e->jlink =p;
}
if(graph->amlist [p->jvex ].firstedge==NULL )
graph->amlist [p->jvex ].firstedge =p;
else
{
q=graph->amlist [p->jvex ].firstedge ;
while(q!=NULL)
{
e=q;
if(q->ivex ==p->ivex )
q=q->ilink ;
else
q=q->jlink ;
}
if(e->ivex ==p->ivex )
e->ilink =p;
else
e->jlink =p;
}
}
}
void SetMark(Graph *graph)//设置访问标记
{
int i;
for(i=1;i<=graph->numberOfVerts ;i++)
graph->amlist [i].mark =0;
}
void DFS(Graph *graph,int v)//深度遍历
{
ENode *p;
printf("%d ",v);
graph->amlist [v].mark =1;
p=graph->amlist [v].firstedge ;
while(p!=NULL)
{
if(p->ivex ==v)
{
if(graph->amlist [p->jvex ].mark ==0)
{
printf("<%d,%d>\n",p->ivex ,p->jvex );
DFS(graph,p->jvex );
}
p=p->ilink ;
}
else
{
if(graph->amlist [p->ivex].mark ==0)
{
printf("<%d,%d>\n",p->jvex ,p->ivex );
DFS(graph,p->ivex );
}
p=p->jlink ;
}
}
}
void BFS(Graph *graph,int u)//广度遍历
{
LinkQueue Q;
ENode *p;
InitQueue(&Q);
printf("%d ",u);
graph->amlist [u].mark =1;
QueueAppend(&Q,u);
while(Q.front !=Q.rear )
{
QueueDelete(&Q,&u);
p=graph->amlist [u].firstedge ;
while(p!=NULL)
{
if(p->ivex ==u)
{
if(graph->amlist [p->jvex ].mark ==0)
{
QueueAppend(&Q,p->jvex );
graph->amlist [p->jvex ].mark =1;
printf("<%d,%d>\n",p->ivex ,p->jvex );
printf("%d ",p->jvex );
}
p=p->ilink ;
}
else
{
if(graph->amlist [p->ivex ].mark ==0)
{
QueueAppend(&Q,p->ivex );
graph->amlist [p->ivex ].mark =1;
printf("<%d,%d>\n",p->jvex ,p->ivex );
printf("%d ",p->ivex );
}
p=p->jlink ;
}
}
}
}
int main()
{
int v1,v2;
Graph graph;
char order;
Initilized(&graph);
CreateGraph(&graph);
printf("\n输入深度广度遍历的起始点:\n");
scanf("%d",&v2);
v1=v2;
printf("\n深度遍历序列及相应的生成树:\n顶点序列: 生成树边集:\n");
SetMark(&graph);
DFS(&graph,v2);
printf("\n广度遍历序列及相应生成树:\n顶点序列:生成树边集:\n");
SetMark(&graph);
BFS(&graph,v1);
return 0;
}