图的广度遍历课程设计报告.docx
《图的广度遍历课程设计报告.docx》由会员分享,可在线阅读,更多相关《图的广度遍历课程设计报告.docx(26页珍藏版)》请在冰豆网上搜索。
![图的广度遍历课程设计报告.docx](https://file1.bdocx.com/fileroot1/2023-1/31/6c75af0b-af36-454b-984e-f9794e1959b4/6c75af0b-af36-454b-984e-f9794e1959b41.gif)
图的广度遍历课程设计报告
目录
1.需求分析3
1)问题描述3
2)系统功能3
2.概要设计4
1)流程图4
2)结构体、函数及说明4
3.详细设计6
1)遍历函数设计6
2)容错性方法设计7
3)生成邻接表设计7
4)邻接表遍历设计8
4.调试分析和测试结果9
1)调试分析9
2)测试结果9
a)测试案例9
b)测试过程与截图9
i.容错性测试10
ii.无向图(案例一)测试13
iii.有向图(案例二)测试15
5.总结17
参考文献18
附录19
1.需求分析
1)问题描述
对任意给定的图(顶点数和边数自定),建立它的邻接表并输出,然后利用队列的五种基本运算(置空队列、进队、出队、取队头元素、判队空)实现图的广度优先搜索遍历。
2)系统功能
首先输入图的类型,有向或无向图(因为遍历与权值无关,所以没有涉及带权图)。
然后输入图的顶点数、边数和各条边,之后生成该图的邻接表并输出。
再输入要遍历该图的起点,然后从所输入的点广度搜索该图的邻接表,并按遍历顺序输出顶点内容。
之后决定是否继续遍历该图或输入另一个需要遍历的图亦或是结束程序。
2.
概要设计
1)流程图
是
否
否
是
2)结构体、函数及说明
typedefstructArcNode//邻接表表结点
{
intadjvex;//该弧所指向的顶点位置
structArcNode*nextarc;//指向下一条弧的指针
//InfoType*info;//该弧相关信息指针
}ArcNode;
typedefstructVNode//邻接表头结点
{
VertexTypedata;//结点信息
ArcNode*firstarc;//指向第一条依附该结点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM];
typedefstruct//图
{
AdjListVertices;//邻接表头结点数组
intvexnum,arcnum;//图的当前顶点数和弧数
intkind;//图的种类标志(有向图:
0,无向图:
1)
}ALGraph;
voidprint(intv)//输出顶点信息
intFirstAdjVex(ALGraphG,intu)//在邻接表G中取第u个头结点
voidNextAdjVex(ALGraphG,intu,intw)//在邻接表G中取第u个头结点之后的结点w的下一结点
voidBFSTraverse(ALGraphG,queueQ,boolvisited[],intm,intn,void(*Visit)(int))//使用辅助队列Q从邻接表结点m开始n结束广度遍历邻接表图G
3.
详细设计
1)遍历函数设计
voidBFSTraverse(ALGraphG,queueQ,boolvisited[],intm,intn,void(*Visit)(int))//使用辅助队列Q从邻接表结点m开始n结束广度遍历邻接表图G
{
//按广度优先非递归遍历图G。
使用辅助队列Q和访问标志数组visited。
while(!
Q.empty())Q.pop();//置空的辅助队列Q
for(intv=m;vif(!
visited[v])//如果v尚未访问
{
visited[v]=true;
Visit(v);//访问v
Q.push(v);//v入队列
while(!
Q.empty())
{
intu=Q.front();
Q.pop();//对头元素出队并置为u
for(intw=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))//从以u为头结点的邻接表开始遍历到链表中的最后一个结点
if(!
visited[w])//w为u的尚未访问的邻接结点
{
visited[w]=true;
Visit(w);//访问w
Q.push(w);//w入队列
}//if
}//while
}//if
}//BFSTraverse*/
2)容错性方法设计
cout<<"\n请输入图的弧数:
";
for(;;)
{
gets_s(s);//以字符串形式接受所输入的数据
t=atoi(s);//取出字符串中的整型
n=G.vexnum*(G.vexnum-1)/2;//计算整型合理范围
if(G.kind==1)
n+=n;//修正整型合理范围
if(t>n||t<1)//如果整型不在合理范围内
cout<<"错误!
请输入一个大于0小于"<";//程序报错并返回循环重新输入
else//如果整型在范围内
break;//跳出循环继续
}
G.arcnum=t;//赋值
3)生成邻接表设计
cout<<"\n请分别输入图的"<"<for(inti=0;i{
intn1,n2;
for(;;)
{
cout<<"弧"<
\t";
gets_s(s);//以字符串形式接受所输入的数据
t=atoi(s);//取出字符串中的整型
strcpy_s(c,s+2);//去掉字符串中的前两个字符
n=atoi(c);//取出字符串中的整型
if(t>G.vexnum||t<1||n>G.vexnum||n<1||t==n)//如果整型不在合理范围内
cout<<"错误!
请分别输入两个大于0小于"<\n\n";//程序报错并返回循环重新输入
else//如果整型在范围内
break;//跳出循环继续
}
n1=t-1;n2=n-1;//修正赋值
if(!
visited[n1])//如果n1后没有结点
{
visited[n1]=true;//设置n1后已有结点
G.Vertices[n1].firstarc=newArcNode;//在n1后增加新结点
G.Vertices[n1].firstarc->adjvex=n2;//结点赋值为n2
G.Vertices[n1].firstarc->nextarc=NULL;//设置n2之后的结点为空
}
else//如果n1后已有结点
{
ArcNode*p=G.Vertices[n1].firstarc;//新建结点指针
while((p->nextarc)!
=NULL)
p=p->nextarc;//将指针指向链表中最后一个结点
p->nextarc=newArcNode;//在最后增加一个新结点
p->nextarc->adjvex=n2;//给结点赋值n2
p->nextarc->nextarc=NULL;//设置n2之后的结点为空
}
}
4)邻接表遍历设计
cout<<"\n\n请输入遍历的起点:
";
for(;;)
{
gets_s(s);//以字符串形式接受所输入的数据
t=atoi(s);//取出字符串中的整型
if(t>G.vexnum||t<1)//如果整型不在合理范围内
cout<<"错误!
请输入一个大于0小于"<";//程序报错并返回循环重新输入
else//如果整型在范围内
break;//跳出循环继续
}
t-=1;//赋值
cout<<"\n广度遍历顺序:
\n";
queueQ;//定义辅助队列Q
for(intj=0;jvisited[j]=false;//访问标志置0
BFSTraverse(G,Q,visited,t,G.vexnum,print);//从输入的遍历起点到最后一个邻接表头结点遍历邻接表
BFSTraverse(G,Q,visited,0,t,print);//从第一个邻接表头结点到输入的邻接表头结点遍历邻接表,防止还有连通分量尚未遍历
4.
调试分析和测试结果
编译、运行及调试环境:
MicrosoftVisualStudio2010Ultimate
1)调试分析
一.在求图的第u个顶点,与其相邻的一系列顶点中,第w个顶点的下一个顶点时,若是求最后一个顶点的下一个顶点时,因为是空指针所以返回值为0,程序误以为是第一个顶点,再求下一个顶点时便报错。
原因是判断条件没有写好,于是增加判断,当指针为空时返回-1。
修改判断条件后,函数正常运行。
二.在输入图信息的时候,若输入非法字符,程序会异常终止。
例如程序要求输入一个整型,用户却输入了一个字母,这时候会出现异常。
只是程序是否健壮性的一个体现。
先用字符串接收字符,转换成整型后再判断是否符合要求。
如果不符合便提示用户按要求重新输入。
还有其他一些类似的输入异常,都是采用类似的处理方法。
三.作为一个完整的程序,友好的界面是必须的。
因次程序中适当地采用系统中的清屏命令,使得界面更加简洁,明了。
2)测试结果
a)测试案例:
案例一案例二
b)测试过程与截图:
i.容错性测试:
在输入字母、符号、或不符合要求的数字时,提示错误并要求重新正确输入。
(截图1.1~截图1.7)
截图1.1
截图1.2
截图1.3
截图1.4
截图1.5
截图1.6
截图1.7
ii.
无向图(案例一)测试:
将测试案例一的数据输入程序,从第一个顶点开始遍历,结果如截图2.1所示。
截图2.1
输入y表示要继续遍历该图后,程序清屏,再次打印邻接表,并要求再次输入遍历起点,如截图2.2所示。
截图2.2
输入2后,打印从第二个顶点遍历的顺序,输入n停止继续遍历该图,如截图2.3所示。
截图2.3
iii.
有向图(案例二)测试:
输入y表示继续输入其他图进行遍历后,程序清屏,并要求再次输入一个图,将测试案例2的数据输入程序后,从第一个顶点遍历,如截图3.1所示。
截图3.1
输入y表示要继续遍历该图后,程序清屏,再次打印邻接表,并要求再次输入遍历起点。
输入4后,打印从第四个顶点遍历的顺序,输入n停止继续遍历该图。
再次输入n停止继续输入其他图进行遍历,程序结束,如截图3.2所示。
截图3.2
5.
总结
通过这一个课程设计,使我对图的广度优先遍历算法有了深度的了解。
在编程过程中也遇到了一些问题,基本上都一一解决了。
但是由于能力有限,程序中还有一些尚未解决的问题。
例如,如何能完全解决输入异常。
这说明今后还需要继续努力。
参考文献
a.图书类的参考文献
1.谭浩强·C语言程序设计·(第2版)·清华大学出版社,出版年:
2009引用部分起止页码:
50-51,248-258。
2.陈维兴,林小茶·C++面向对象程序设计教程·(第3版)·清华大学出版社,出版年:
2009引用部分起止页码:
260-273。
3.严蔚敏,吴伟民·数据结构·(C语言版)·清华大学出版社,出版年:
2010引用部分起止页码:
156-170。
4.梁旭,谷晓琳,黄明·C语言课程设计·(第2版)·电子工业出版社,出版年:
2009引用部分起止页码:
21-24。
b.翻译图书类的参考文献
1.埃克尔著·C++编程思想·刘宗田等译·(第2版)·机械工业出版社,出版年:
2003。
c.网站的名称
谷歌:
www.G
XX:
附录
程序源码:
//题目十一.图的广度遍历
//问题描述:
对任意给定的图(顶点数和边数自定),建立它的邻接表并输出,
//然后利用队列的五种基本运算(置空队列、进队、出队、取队头元素、判队空)实现图的广度优先搜索遍历。
#include
#include
#defineMAX_VERTEX_NUM101
#defineVertexTypeint
usingnamespacestd;
typedefstructArcNode//邻接表表顶点
{
intadjvex;//该弧所指向的顶点位置
structArcNode*nextarc;//指向下一条弧的指针
//InfoType*info;//该弧相关信息指针
}ArcNode;
typedefstructVNode//邻接表头顶点
{
VertexTypedata;//顶点信息
ArcNode*firstarc;//指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM];
typedefstruct//图
{
AdjListVertices;//邻接表头顶点数组
intvexnum,arcnum;//图的当前顶点数和弧数
intkind;//图的种类标志(有向图:
0,无向图:
1)
}ALGraph;
voidprint(intv)//输出顶点信息
{
cout<}
intFirstAdjVex(ALGraphG,intu)//在邻接表G中取第u个头顶点
{
if(G.Vertices[u].firstarc==NULL)
return-1;
returnG.Vertices[u].firstarc->adjvex;
}
intNextAdjVex(ALGraphG,intu,intw)//在邻接表G中取第u个头顶点之后的顶点w的下一顶点
{
ArcNode*p=G.Vertices[u].firstarc;
while(w!
=p->adjvex)
p=p->nextarc;
if(p->nextarc==NULL)return-1;
returnp->nextarc->adjvex;
}
voidBFSTraverse(ALGraphG,queueQ,boolvisited[],intm,intn,void(*Visit)(int))//使用辅助队列Q从邻接表顶点m开始n结束广度遍历邻接表图G
{
//按广度优先非递归遍历图G。
使用辅助队列Q和访问标志数组visited。
while(!
Q.empty())Q.pop();//置空的辅助队列Q
for(intv=m;vif(!
visited[v])//v尚未访问
{
visited[v]=true;
Visit(v);
Q.push(v);//v入队列
while(!
Q.empty())
{
intu=Q.front();
Q.pop();//对头元素出队并置为u
for(intw=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
if(!
visited[w])//w为u的尚未访问的邻接顶点
{
visited[w]=true;
Visit(w);
Q.push(w);
}//if
}//while
}//if
}//BFSTraverse*/
intmain()
{
intt,n;
chars[10],c[10];
ALGraphG;
do
{
system("cls");
cout<<"请输入图的类型(有向图输1,无向图输2):
";
for(;;)
{
gets_s(s);
t=atoi(s);
if(t!
=1&&t!
=2)
cout<<"错误!
请输入半角数字\"1\"或\"2\":
";
else
break;
}
G.kind=t;
cout<<"\n请输入图的顶点数:
";
for(;;)
{
gets_s(s);
t=atoi(s);
if(t>99||t<1)
cout<<"错误!
请输入一个大于0小于100的数:
";
else
break;
}
G.vexnum=t;
boolvisited[MAX_VERTEX_NUM];
for(inti=0;i{
visited[i]=false;
G.Vertices[i].firstarc=NULL;
}
if(G.kind==1)
{
cout<<"\n请输入图的弧数:
";
for(;;)
{
gets_s(s);
t=atoi(s);
n=G.vexnum*(G.vexnum-1)/2;
if(G.kind==1)
n+=n;
if(t>n||t<1)
cout<<"错误!
请输入一个大于0小于"<";
else
break;
}
G.arcnum=t;
cout<<"\n请分别输入图的"<"<for(inti=0;i{
intn1,n2;
for(;;)
{
cout<<"弧"<
\t";
gets_s(s);
strcpy_s(c,s);
t=atoi(c);
strcpy_s(c,s+2);
n=atoi(c);
if(t>G.vexnum||t<1||n>G.vexnum||n<1||t==n)
cout<<"错误!
请分别输入两个大于0小于"<\n\n";
else
break;
}
n1=t-1;n2=n-1;
if(!
visited[n1])
{
visited[n1]=true;
G.Vertices[n1].firstarc=newArcNode;
G.Vertices[n1].firstarc->adjvex=n2;
G.Vertices[n1].firstarc->nextarc=NULL;
}
else
{
ArcNode*p=G.Vertices[n1].firstarc;
while((p->nextarc)!
=NULL)
p=p->nextarc;
p->nextarc=newArcNode;
p->nextarc->adjvex=n2;
p->nextarc->nextarc=NULL;
}
}
}
else
{
cout<<"\n请输入图的边数:
";
for(;;)
{
gets_s(s);
t=atoi(s);
n=G.vexnum*(G.vexnum-1)/2;
if(G.kind==1)
n+=n;
if(t>n||t<1)
cout<<"错误!
请输入一个大于0小于"<";
else
break;
}
G.arcnum=t;
cout<<"\n请分别输入图的"<"<for(inti=0;i{
intn1,n2;
for(;;)
{
cout<<"边"<
\t";
gets_s(s);
strcpy_s(c,s);
t=atoi(c);
strcpy_s(c,s+2);
n=atoi(c);
if(t>G.vexnum||t<1||n>G.vexnum||n<1||t==n)
cout<<"错误!
请分别输入两个大于0小于"<\n\n";
else
break;
}
n1=t-1;n2=n-1;
if(!
visited[n1])
{
visited[n1]=true;
G.Vertices[n1].firstarc=newArcNode;
G.Vertices[n1].firstarc->adjvex=n2;
G.Vertices[n1].firstarc->nextarc=NULL;
}
else
{
ArcNode*p=G.Vertices[n1].firstarc;
while((p->nextarc)!
=NULL)
p=p->nextarc;
p->nextarc=newArcNode;
p->nextarc->adjvex=n2;
p->nextarc->nextarc=NULL;
}
if(!
visited[n2])
{
visited[n2]=true;
G.Vertices[n2].firstarc=newArcNode;
G.Vertices[n2].firstarc->adjvex=n1;