图的基本操作与实现Word格式文档下载.docx
《图的基本操作与实现Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《图的基本操作与实现Word格式文档下载.docx(31页珍藏版)》请在冰豆网上搜索。
对于带权图,结点中还要保存该边的权值cost。
通过在顶点表的第i个顶点信息中保存的指针adj,可以找到与顶点i对应的边链表的第一个边结点;
此外,该记录还保存有该顶点的其他信息。
1.3.2图的深度优先搜索
深度优先搜索是个不断探查和回溯的过程。
在探查的每一步,算法都有一个当前顶点。
最初的当前顶点,也就是指定的起始顶点。
每一步探查过程中,首先对当前顶点v进行访问,并立即设置该顶点的访问标志visited[v]=true。
接着在v的所有邻接顶点中,找出尚未访问过的一个,将其作为下一步探查的当前顶点。
倘若当前顶点的所有邻接顶点都已经被访问过,则退回一步,将前一步所访问的顶点重新取出,当作探查的当前顶点。
重复上述过程,直到最初指定起始顶点的所有邻接顶点都被访问到,此时连通图中的所有顶点也必然都被访问过了。
1.3.3图的广度优先搜索
广度优先搜索时一个逐层遍历的过程,在此过程中,图中有多少顶点就要重复多少步。
每一步都有一个当前顶点。
最初的当前顶点是主过程指定的起始顶点。
在每一步中,首先访问当前顶点v,并设置该顶点的访问标志visited[v]=true。
接着依次访问v的各个未曾被访问过的邻接顶点w1,w2,…,wt,然后再顺序访问w1,w2,…,wt的所有还未被访问过的邻接顶点。
再从这些访问过的顶点出发,再访问它们的所有还未被访问过的邻接顶点,如此做下去,直到图中所有顶点都被访问为止。
2概要设计
2.1程序的整体功能结构
输入1个图先求出每个顶点的度,输出结果;
然后指定任意顶点x为初始顶点,对图G作DFS遍历,输出DFS顶点序列;
接着指定任意顶点x为初始顶点,对图G作BFS遍历,输出BFS顶点序列;
其次输入顶点x,查找图G:
下一步是判断图G是否是连通图,输出信息“YES”/“NO”;
最后如果选用的存储结构是邻接矩阵,则用邻接矩阵的信息生成图G的邻接表,即复制图G,然再执行操作
(2);
反之亦然。
2.2数据结构的设计
2.2.1边节点类的定义
structEdge//边结点的定义
{
intdest;
//边的另一顶点位置
Ecost;
//边上的权值
Edge<
T,E>
*link;
//下一条边链指针
};
2.2.2顶点类的定义
template<
classT,classE>
//顶点的定义
structVertex
Tdata;
//顶点的名字
Edge<
*adj;
//边链表的头指针
2.2.3图类的定义
classGraph//图的类定义
{
protected:
intmaxVertices;
//图中最大的顶点数
intnumEdges;
//当前边数
intnumVertices;
//当前顶点数
T*output;
//存放遍历的数组
T*input;
//存放输入数组
Vertex<
*NodeTable;
//顶点表(各边链表的头结点)
intgetVertexPos(constTvertx)//取顶点v在数组中的位置
{
intj=-1;
for(inti=0;
i<
numVertices;
i++)
{
if(NodeTable[i].data==vertx)
j=i;
}
returnj;
}
voidDFS(Graph<
&
G,intv,boolvisited[])//图的深度优先搜索
cout<
<
G.getValue(v)<
'
'
;
//访问顶点v
visited[v]=true;
//作访问标记
intw=G.getFirstNeighbor(v);
//第一个邻接顶点
while(w!
=-1)//若邻接顶点w存在
{
if(!
visited[w])
DFS(G,w,visited);
//若w未访问过,递归访问顶点w
w=G.getNextNeighbor(v,w);
//下一个邻接顶点
public:
Graph();
//构造函数
~Graph();
//析构函数
TgetValue(inti)//取顶点i的值
{
return(i>
=0&
i<
numVertices)?
NodeTable[i].data:
0;
boolinsertVertex(constT&
vertex);
//插入顶点vertex
boolinsertEdge(intv1,intv2,Ecost);
//插入边(v1,v2),权值为cost
boolremoveVertex(intv);
//删除指定的顶点
boolremoveEdge(intv1,intv2);
//删除一条边
intgetFirstNeighbor(intv);
//取顶点v的第一个邻接顶点
intgetNextNeighbor(intv,intw);
//取v的邻接顶点w的下一邻接顶点
intgetFirstCost(intv);
//取顶点v的第一个邻接顶点的cost值
intgetNextCost(intv,intw);
//取v的邻接顶点w的下一邻接顶点的cost值
voidDFS(Graph<
G,constT&
v);
//从顶点v出发对图G进行深度优先遍历的主过程
intBFS(Graph<
T,E>
G,constT&
//图的广度优先搜索
voidWheCan(Graph<
G);
//判断是否为连通图
voidOutPut();
//输出
voidHaveEdge(Graph<
//求顶点的度
voidSerachVertex(Graph<
//输入顶点x,查找图G:
若存在含x的顶点,则删除该结点及与之相关连的边,并作DFS遍历
voidChangeGraph(Graph<
//将用邻接表表示的数转换为邻接矩阵表示
voidInput();
//输入
3详细设计和实现
3.1算法流程图
程序主要设计了六个功能:
首先是求每个顶点的度,然后可以选择对图G作DFS(或BFS)搜索,接着可以判断此图是否连通,接着可以将图G转换为临街矩阵存储方式退出,最后可以对图G作查找顶点。
主函数流程如下:
图3.1.1主函数流程图
3.2各个要求的实现方法
3.2.1自选存储结构,输入含n个顶点(用字符表示顶点)和e条边的图G
采用邻接表的存储结构
N个顶点的输入存储到顶点节点链表(Vertex)中
如果第n个节点和第m个节点之间含有一条边e,就将n和m的顶点链表中指向的边链表中存储入n和m在顶点表中的下标和权值
3.2.2求每个顶点的度,输出结果
顶点的度指与该顶点相关联的边的条数
在用邻接链表做为图的存储方式中,要求一个顶点n的度只要去搜索存放顶点n的边节点链表,其中存放了多少条边的信息,这个顶点的度就为多少。
3.2.3指定任意顶点x为初始顶点,对图G作DFS遍历,输出DFS顶点序列
DFS遍历指的是深度优先搜索
深度优先搜索的基本思想:
DFS在访问图中某一起始顶点v后,由v出发,访问它的任一邻接顶点w1;
再从w1出发,访问与w1邻接但还没有访问过的顶点w2;
然后再从w2出发,进行类似的访问,…如此进行下去,直至到达所有的邻接顶点都被访问过的顶点u为止。
接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。
如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;
如果没有,就再退回一步进行搜索。
重复上述过程,直到连通图中所有顶点都被访问过为止。
3.2.4指定任意顶点x为初始顶点,对图G作BFS遍历,输出BFS顶点序列
BFS指的是广度优先搜索
BFS基本思想:
BFS在访问了起始顶点v之后,由v出发,依次访问v的各个未被访问过的邻接顶点w1,w2,…,wt,然后再顺序访问w1,w2,…,wt的所有还未被访问过的邻接顶点。
再从这些访问过的顶点出发,再访问它们的所有还未被访问过的邻接顶点,…如此做下去,直到图中所有顶点都被访问到为止。
广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况。
因此,广度优先搜索不是一个递归的过程。
3.2.5输入顶点x,查找图G:
输入顶点,在顶点表中所搜是否含有这个顶点,如果没有,就输出“无x”。
如果含有,搜索存放顶点n的边节点链表,找出其中存放的顶点m,然后将这个顶点m链表中的存放n的那个节点删除,同时将n中存放m的节点删除。
然后在顶点链表中存放顶点n的节点删除。
3.2.6判断图G是否是连通图,输出信息“YES”/“NO”;
对图做BFS遍历,如果遍历到的顶点数等于当前的顶点数的个数,这个图就是联通图,反之就不是连通图
3.2.7如果选用的存储结构是邻接矩阵,则用邻接矩阵的信息生成图G的邻接表,即复制图G,然再执行操作
(2);
反之亦然
我采用的是邻接矩阵做为图的存储结构。
将用邻接表存储的图转化为邻接矩阵的存储的基本思想是:
(1)将图的顶点表中存放的顶点的信息都存放在一个顶点矩阵中
(2)逐个搜索各个顶点的边节点链表,如果含有节点,将邻接矩阵中对应二维数组的值赋值为cost的值。
顶点的度为:
统计第i行(列)不为0的个数可得顶点i的度。
3.3主程序设计
///////////////////////////////////////////////////Graph.h
#include<
iostream>
stdlib.h>
#include"
Queue.h"
usingnamespacestd;
intdest;
Ecost;
Edge(){}//构造函数
Edge(intnum,Ecost):
dest(num),weight(cost),link(NULL){}//构造函数
booloperator!
=(Edge<
R)const//判边等否
{
returndest!
=R.dest;
Tdata;
Edge<
Graph<
:
Graph()//构造函数:
建立一个空的邻接表
{
maxVertices=100;
numVertices=0;
numEdges=0;
NodeTable=newVertex<
[maxVertices];
//创建顶点表数组
if(NodeTable==NULL)
cerr<
"
存储分配错!
endl;
exit
(1);
for(inti=0;
maxVertices;
i++)
NodeTable[i].adj=NULL;
output=newT[maxVertices];
}
~Graph()//析构函数:
删除一个邻接表
*p=NodeTable[i].adj;
while(p!
=NULL)
{
NodeTable[i].adj=p->
link;
deletep;
p=NodeTable[i].adj;
}
delete[]NodeTable;
//删除顶点表数组
boolGraph<
insertVertex(constT&
vertex)//插入顶点
if(numVertices==maxVertices)
returnfalse;
NodeTable[numVertices].data=vertex;
numVertices++;
returntrue;
template<
classT,classE>
insertEdge(intv1,intv2,Ecost)//插入边(v1,v2),权值为cost
if(v1>
=0&
v1<
=numVertices&
v2>
v2<
=numVertices)
*q,*p=NodeTable[v1].adj;
//v1对应的边链表头z指针
while(p!
=NULL&
p->
dest!
=v2)//寻找邻接顶点v2
p=p->
if(p!
=NULL)//找到此边不插入
returnfalse;
p=newEdge<
//否则创建新节点
q=newEdge<
p->
dest=v2;
cost=cost;
link=NodeTable[v1].adj;
//链入v1的边链表
NodeTable[v1].adj=p;
q->
dest=v1;
link=NodeTable[v2].adj;
//链入v2的边链表
NodeTable[v2].adj=q;
numEdges++;
returntrue;
else
参数有误!
请重新输入!
removeVertex(intv)
if(numVertices==1||v<
0||v>
参数有误,请重新输入!
//表空或顶点超出范围
*p,*s,*t;
intk;
while(NodeTable[v].adj!
=NULL)
p=NodeTable[v].adj;
k=p->
dest;
s=NodeTable[k].adj;
t=NULL;
while(s!
s->
=v)
t=s;
s=s->
if(s!
if(t==NULL)
NodeTable[k].adj=s->
else
t->
link=s->
deletes;
NodeTable[v].adj=p->
deletep;
numEdges--;
numVertices--;
NodeTable[v].data=NodeTable[numVertices].data;
p=NodeTable[v].adj=NodeTable[numVertices].adj;
while(p!
s=NodeTable[p->
dest].adj;
if(s->
dest==numVertices)
{
s->
dest=v;
break;
}
s=s->
removeEdge(intv1,intv2)
if(v1!
=-1&
v2!
=-1)
*p=NodeTable[v1].adj,*q=NULL,*s=p;
des