图的遍历实验报告附C++源码.docx
《图的遍历实验报告附C++源码.docx》由会员分享,可在线阅读,更多相关《图的遍历实验报告附C++源码.docx(13页珍藏版)》请在冰豆网上搜索。
图的遍历实验报告附C++源码
图的遍历
一、问题背景
若用有向网表示网页的链接网络,其中顶点表示某个网页,有向弧表示网页之间的链接关系。
试设计一个网络蜘蛛系统,分别以广度优先和深度优先的策略抓取网页。
二、需求分析
1)首先输入顶点的数量,然后是各顶点对应的字母,再输入各条弧(权值都置为1);
2)输出从首个顶点开始的广度优先遍历序列和深度先遍历序列;
3)为了达到任意图的遍历(结点名称不一定是数字,可以是任意可见字符),可以自定义一个数组类型,保存该结点的名称和记录是否被访问;
4)图使用相邻矩阵来实现;
5)测试数据:
输入
输入顶点数和弧数:
89
输入8个顶点.
输入顶点0:
a
输入顶点1:
b
输入顶点2:
c
输入顶点3:
d
输入顶点4:
e
输入顶点5:
f
输入顶点6:
g
输入顶点7:
h
输入9条弧.
输入弧0:
ab1
输入弧1:
bd1
输入弧2:
be1
输入弧3:
dh1
输入弧4:
eh1
输入弧5:
ac1
输入弧6:
cf1
输入弧7:
cg1
输入弧8:
fg1
输出
广度优先遍历:
abdhecfg
深度优先遍历:
abcdefgh
三、概要设计
抽象数据类型
为了遍历任意图,定义了如下数据类型,用于存储该结点的名称和记录是否被访问过。
classNode//基本抽象数据类型
{
public:
charch;//记录名称,如果将这里改成数组,结点名称可以是多个字符
intflag;//记录结点是否被访问
};
classGraph//图类,此类中,封装了图的一些成员和一些必须的成员函数
{
private:
intgetSub(char);//获取某名称的下标
Node*arrNode;//记录名称和是否访问的数组
intnumVertex,numEdge;//记录图的顶点数和边数
int**matrix;//用一个二维数组记录两点间是否相连,1相连,0断开
public:
voidsetCh(char,int);//将数组的arrNode的每一个单元设置一个结点名称
Graph(int);
~Graph();
intgetNumVertex();//获得图的顶点数
charfirst(charch);//获得相邻结点
charnext(charch1,charch2);//获得隔着ch2,但与ch2相邻的结点
voidsetEdge(char,char,intw=1);//设置两顶点的边和权重(权重默认为1)
intgetMark(char);//获取是否被访问的记录,已访问返回1,未访问返回0
voidsetMark(char);//把已访问的结点,设置标记
};
算法的基本思想
用一个自定义类型的数组,记录每个结点的信息(包括名称、是否被访问),且此数组作为图的成员变量之一;用一个类,对数组进行相应的操作,以便获得所需的信息。
深度优先采取的递归思想。
首先,将从起点,沿某条边,顺势遍历下去,直到不能继续遍历下去。
这时,又从起点的另一结点开始,遍历下去。
如此往复,知道将所有结点遍历完。
广度优先得使用队列。
首先,将起点入队,并标为已访问。
进入循环,当队列不为空时,出队,输出,并将与出队的元素相邻的且未访问的结点全部放入队列,标为已访问。
一次循环,只有一个结点出队,大于等于0个结点入队。
voidDFS(Graph*G,charch)//深度优先遍历图
{
inti=0;
cout<G->setMark(ch);
for(charw=G->first(ch);igetNumVertex();w=G->next(ch,w))
{
if(G->getMark(w)==0)
DFS(G,w);
i++;
}
}
voidBFS(Graph*G,charch,myQueue*q)//广度优先遍历图
{
charv,w;
q->enqueue(ch);
G->setMark(ch);
while(q->length()!
=0)
{
q->dequeue(v);
cout<inti=0;
for(w=G->first(v);igetNumVertex();w=G->next(v,w))
{
if(G->getMark(w)==0)
{
G->setMark(w);
q->enqueue(w);
}
i++;
}
}
}
程序的流程
输入顶点数、边数——>完成图的初始化——>输入顶点名称、设置边——>输入遍历起点——>深度优先遍历,输出结果——>广度优先遍历,输出结果
算法的时空分析
因为此程序追求结点的个性化(可以不按ABCD……的顺序来命名,可以用任意可见字符),使得程序的时间代价比较大。
不管是设置顶点名称,还是遍历,这都与图的顶点个数相关,包括根据结点名称获得下标的函数也是用循环实现的,时间复杂度为Θ(n)。
总之,时间开销比较大。
输入和输出的格式
输入
输入顶点数和弧数:
//提示输入
等待输入
输入8个顶点.//提示
输入顶点0:
//提示输入
等待输入
……
输入9条弧.//提示
输入弧0:
//提示输入
等待输入
四、运行截图
五、实验心得(可选)
这个实验,总的难度不大。
但我在追求结点名称的个性化的时候,没注意到这带来的时间开销,在写实验报告时才发现。
实验中,大量使用了循环,所以比较适合于密集图,稀疏图用此,那就是程序,那就是浪费时间了。
发现这个程序,还存在不完善的地方。
如果图不是连通的,那么将不能把所有的点遍历完。
六、附录(源码)
/*********************各类定义******************************/
classNode//基本抽象数据类型
{
public:
charch;//记录名称,如果将这里改成数组,结点名称可以是多个字符
intflag;//记录结点是否被访问
};
classGraph//图类,此类中,封装了图的一些成员和一些必须的成员函数
{
private:
intgetSub(char);//获取某名称的下标
Node*arrNode;//记录名称和是否访问的数组
intnumVertex,numEdge;//记录图的顶点数和边数
int**matrix;//用一个二维数组记录两点间是否相连,1相连,0断开
public:
voidsetCh(char,int);//将数组的arrNode的每一个单元设置一个结点名称
Graph(int);
~Graph();
intgetNumVertex();//获得图的顶点数
charfirst(charch);//获得相邻结点
charnext(charch1,charch2);//获得隔着ch2,但与ch2相邻的结点
voidsetEdge(char,char,intw=1);//设置两顶点的边和权重(权重默认为1)
intgetMark(char);//获取是否被访问的记录,已访问返回1,未访问返回0
voidsetMark(char);//把已访问的结点,设置标记
};
/*************各函数的函数体*****************************/
Graph:
:
Graph(intn)
{
inti,j;
arrNode=newNode[n];
numVertex=n;
numEdge=0;
for(i=0;iarrNode[i].flag=0;
matrix=newint*[numVertex];
for(i=0;imatrix[i]=newint[numVertex];
for(i=0;ifor(j=0;jmatrix[i][j]=0;
}
Graph:
:
~Graph()
{
delete[]arrNode;
for(inti=0;idelete[]matrix[i];
delete[]matrix;
}
intGraph:
:
getSub(charch)//获取下标
{
for(inti=0;iif(ch==arrNode[i].ch)
returni;
}
intGraph:
:
getNumVertex()//获取顶点数
{
returnnumVertex;
}
charGraph:
:
first(charch)//获取相邻结点
{
for(inti=0;iif(matrix[getSub(ch)][i]!
=0)
returnarrNode[i].ch;
returnch;
}
charGraph:
:
next(charch1,charch2)//获取与ch2相邻的结点
{
for(inti=getSub(ch2)+1;iif(matrix[getSub(ch1)][i]!
=0)
returnarrNode[i].ch;
returnch1;
}
voidGraph:
:
setEdge(charch1,charch2,intw)//设置边
{
if(matrix[getSub(ch1)][getSub(ch2)]==0)
{
numEdge++;
matrix[getSub(ch1)][getSub(ch2)]=1;
}
}
intGraph:
:
getMark(charch)//获取访问信息
{
for(inti=0;iif(ch==arrNode[i].ch)
returnarrNode[i].flag;
return-1;
}
voidGraph:
:
setMark(charch)//设置标记
{
for(inti=0;iif(ch==arrNode[i].ch)
arrNode[i].flag=1;
}
voidGraph:
:
setCh(charch,intn)//将结点名称设置在字符中
{
arrNode[n].ch=ch;
}
/***************主函数*********************/
voidDFS(Graph*,char);//深度优先遍历函数声明
voidBFS(Graph*,char,myQueue*);//广度优先遍历函数声明
intmain()
{
intnumVer,numE,i;
chartemp1,temp2;//temp1,temp2作临时变量,记录输入的值
myQueue*q=newmyQueue;
cout<<"输入定点数和弧数:
";
cin>>numVer>>numE;
GraphmyGraph1(numVer);
GraphmyGraph2(numVer);
cout<<"请输入"<\n";
for(i=0;i{
cout<<"输入顶点"<
";
cin>>temp1;
myGraph1.setCh(temp1,i);
myGraph2.setCh(temp1,i);
}
cout<<"请输入"<\n";
for(i=0;i{
cout<<"输入弧"<
";
cin>>temp1>>temp2;
myGraph1.setEdge(temp1,temp2);
myGraph2.setEdge(temp1,temp2);
}
cout<<"深度优先结果:
";
DFS(&myGraph1,'a');
cout<<"\n广度优先结果:
";
BFS(&myGraph2,'a',q);
cout<return0;
}
voidDFS(Graph*G,charch)//深度优先遍历函数体
{
inti=0;
cout<G->setMark(ch);
for(charw=G->first(ch);igetNumVertex();w=G->next(ch,w),i++)
if(G->getMark(w)==0)
DFS(G,w);
}
voidBFS(Graph*G,charch,myQueue*q)//广度优先遍历函数体
{
charv,w;
q->enqueue(ch);
G->setMark(ch);
while(q->length()!
=0)
{
q->dequeue(v);
cout<inti=0;
for(w=G->first(v);igetNumVertex();w=G->next(v,w),i++)
if(G->getMark(w)==0)
{
G->setMark(w);
q->enqueue(w);
}
}
}