实验项目六 图结构基本操作的实现(截止12-20)
1.掌握图的基本操作—遍历。
实验要求:
1、 分别用DFS和BFS的方法实现一个无向图的遍历。
实验过程:
1、 创建一个图(可用邻接矩阵或邻接表的方式进行存储);
2、 输入选项:0或1,0为DFS,1为BFS。
3、 分别输出DFS和BFS两种遍历序列;
实验报告中给出DFS和BFS两种遍历的算法代码。
实验结果:
1、输入顶点集:1 2 3 4 5 6 7 8
2、输入边的集合: 1 2
1 3
2 4
2 5
4 8
5 8
3 6
3 7
6 7
输入:0(DFS)
输出:DFS遍历序列为:12485367
输入:1(BFS)
输出:BFS遍历序列为:12345678
实验分析:
1.简单分析DFS与BFS实现时用到的方法(DFS通过递归函数实现,用到栈的数据结构,BFS用到队列的数据结构);
2.列举调试运行过程中出现的错误并分析原因。
要求:
(1) 程序要添加适当的注释,程序的书写要采用缩进格式。
(2) 程序要具在一定的健壮性,即当输入数据非法时,程序也能适当地做出反应。
(3) 程序要做到界面友好,在程序运行时用户可以根据相应的提示信息进行操作。
(4) 上传源程序到课堂派。顺序表的源程序保存为TraversalGraph.cpp。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define MaxInt 32767
#define MVNum 100
#define OK 1
#define ERROR 0
using namespace std;
typedef int Status;
//------队列的链式存储结构-------
typedef struct QNode
{
int data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//链式队列的初始化
Status InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=new QNode;
Q.front->next=NULL;
return OK;
}
//链式队列的入队操作
Status EnQueue(LinkQueue &Q,int e)
{
QueuePtr p;
p=new QNode;
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return OK;
}
//判断队列是否为空
QueueEmpty(LinkQueue Q)
{
if(Q.front==Q.rear) return OK;
else return ERROR;
}
//链队的出队
Status DeQueue(LinkQueue &Q,int &e)
{
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;
delete p;
return OK;
}
//图的邻接矩阵存储
typedef int VerTexType;
typedef int ArcType;
typedef struct
{
VerTexType vexs[MVNum];
ArcType arcs[MVNum][MVNum];
int vexnum,arcnum;
}AMGragh;
typedef AMGragh Gragh;
bool visited[MVNum];
//确定顶点在G中的位置
int LocateVex(AMGragh G,int m)
{
return m-1;
}
//邻接矩阵创建无向网
Status CreateUDN(AMGragh &G)
{
int i,j,k;
int v1,v2;
printf("请输入顶点数和边数(空格隔开):");
cin>>G.vexnum>>G.arcnum;
for(i=0;i<G.vexnum;i++)
{
printf("请输入第%d个顶点的信息:",i+1);
cin>>G.vexs[i];
}
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
G.arcs[i][j]=0;
}
}
for(k=0;k<G.arcnum;k++)
{
printf("请输入一条边依附的顶点(请用空格隔开):");
cin>>v1>>v2;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
G.arcs[i][j]=1;
G.arcs[j][i]=G.arcs[i][j];
}
return OK;
}
void DFS_AM(AMGragh G,int v)
{
cout<<v;
visited[v]=true;
for(int w=0;w<G.vexnum;w++)
if((G.arcs[v-1][w]!=0)&&(!visited[w+1]))
DFS_AM(G,w+1);
}
//寻找v1的第一个邻接点
int FirstAdjVex(Gragh G,int v1)
{
for(int i=0;i<G.vexnum;i++)
{
if(G.arcs[v1-1][i]==1&&!visited[i+1])
{
return i+1;
}
}
return -1;
}
//u相对于w的下一个邻接点
int NexAdjVex(Gragh G1,int u1,int w1)
{
for(int i=0;i<G1.vexnum;i++)
{
if(G1.arcs[u1-1][i]==1&&(i+1)!=w1&&!visited[i+1])
{
return i+1;
}
}
return -1;
}
void BFS(Gragh G,int v)
{
LinkQueue Q;
cout<<v;
visited[v]=true;
InitQueue(Q);
EnQueue(Q,v);
while(!QueueEmpty(Q))
{
int u;
DeQueue(Q,u);
for(int w=FirstAdjVex(G,u);w>=0;w=NexAdjVex(G,u,w))
{
if(!visited[w])
{
cout<<w;
visited[w]=true;
EnQueue(Q,w);
}
}
}
}
int main()
{
AMGragh G;
CreateUDN(G);
bool choose;
printf("如果你想要使用DFS遍历请输入0,如果你想要使用BFS遍历输入1:");
cin>>choose;
if(choose==0) DFS_AM(G,1);
else BFS(G,1);
return 0;
}
/*输入缓存!当有多个输入进入时,如果不小心多输入,有可能会造成错误,使得多输入的数据进入输入缓存区
进而造成程序错误,避免这种情况的方法是:1、相同类型元素尽可能分开输入2、相同类型数据的话,输入一个数据后换行输入另一个数据
一开始输入顶点的时候8个顶点却输成了9个,结果第9个顶点进入缓存区,读取成了v1,最后结果错误
我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际读取操作
从本质上讲,我们从键盘输入的数据并没有直接交给 scanf(),而是暂时保存到缓冲区中,直到我们按下回车键,scanf() 才从缓冲区中读取数据,赋值给变量。如果缓冲区中的数据符合 scanf() 的要求,那么就读取结束;如果不符合要求,那么就继续等待用户输入,或者干脆读取失败。scanf() 匹配到想要的数据后,会将匹配到的数据从缓冲区中删除,而没有匹配到的数据仍然会留在缓冲区中。
正是由于缓冲区的存在,才使得我们能够多输入一些数据,或者一次性输入所有数据。
2、因为下标问题,邻接矩阵中的边是从下标0开始存的,而bfs输出顶点的时候是从1开始的,因此造成了一些错误。
3、没有认真思考程序的每一行代码,原本数据类型是int类型,自己直接抄的书上的代码结果把数据定义成了char类型
*/
这是我自己写的!这是我自己写的!这是我自己写的!
开心的事要说三遍。在写邻接矩阵的时候,耐心的数据结构老师认真给我解答了疑惑,之后经过两个半小时以上的努力,我终于写完了这个程序。这对我来说是意义重大的,欲望的折磨、学习状态的凄惨、个别人的嘲笑,让我处于既努力自信又痛苦的尴尬境地之中,能够独立完成这个在周围人看来不太简单的任务,带给我很大的快乐,那些不如意,此时又算得了什么呢?
数据结构真的真的真的很简单,不会的人可以问问自己,自己真的认真学了吗?
我也逐渐懂得,只有学习、只有经过努力拿到不那么容易拿到的东西,才能真正给你带来快乐。那些容易得到的,大多只是虚幻无意义的快感罢了。