图的基本操作与实现.docx
《图的基本操作与实现.docx》由会员分享,可在线阅读,更多相关《图的基本操作与实现.docx(31页珍藏版)》请在冰豆网上搜索。
![图的基本操作与实现.docx](https://file1.bdocx.com/fileroot1/2022-11/28/f7db8edc-2543-4390-8aee-bc7c49eab4bd/f7db8edc-2543-4390-8aee-bc7c49eab4bd1.gif)
图的基本操作与实现
摘要:
图(Graph)是一种非线性结构,它的每一个顶点可以与多个其它顶点相关联,各顶点之间的关系是任意的。
这种结构的灵活性很强,可以用来描述和求解更多的实际问题,因此得到广泛的应用。
最典型的应用领域有电路分析、寻找最短路线、项目规划、鉴别化合物、统计力学、遗传学、控制论、语言学,以及一些社会科学中。
反过来,也正是由于其限制很少,已不再属于线性结构,因此运用这类结构时需要有更多的技巧。
本课题是在VC++环境下,运用图的性质完成各种基本操作的实现。
关键词:
邻接矩阵;邻接表;深度(广度)优先遍历;连通分量;递归
1需求分析
1.1课程设计题目
(1)自选存储结构,输入含n个顶点(用字符表示顶点)和e条边的图G;
(2)求每个顶点的度,输出结果;
(3)指定任意顶点x为初始顶点,对图G作DFS遍历,输出DFS顶点序列(提示:
使用一个栈实现DFS);
(4)指定任意顶点x为初始顶点,对图G作BFS遍历,输出BFS顶点序列(提示:
使用一个队列实现BFS);
(5)输入顶点x,查找图G:
若存在含x的顶点,则删除该结点及与之相关连的边,并作DFS遍历(执行操作3);否则输出信息“无x”;
(6)判断图G是否是连通图,输出信息“YES”/“NO”;
(7)如果选用的存储结构是邻接矩阵,则用邻接矩阵的信息生成图G的邻接表,即复制图G,然再执行操作
(2);反之亦然。
1.2课程设计任务及要求
1.搜集图方面的资料;
2.负责设计数据结构,画好流程图,编写代码;
3.撰写课程设计报告;
4.参加答辩。
1.3课程设计思想
1.3.1图的邻接表表示
在第i行的单链表中,各结点分别存放与同一个顶点vi关联的各条边。
各结点配有标识dest,指示该边的另一个顶点;还配有指针link,指向同一链表中的下一条边的边结点。
对于带权图,结点中还要保存该边的权值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:
若存在含x的顶点,则删除该结点及与之相关连的边,并作DFS遍历(执行操作3);否则输出信息“无x”;下一步是判断图G是否是连通图,输出信息“YES”/“NO”;最后如果选用的存储结构是邻接矩阵,则用邻接矩阵的信息生成图G的邻接表,即复制图G,然再执行操作
(2);反之亦然。
2.2数据结构的设计
2.2.1边节点类的定义
structEdge//边结点的定义
{
intdest;//边的另一顶点位置
Ecost;//边上的权值
Edge*link;//下一条边链指针
};
2.2.2顶点类的定义
template//顶点的定义
structVertex
{
Tdata;//顶点的名字
Edge*adj;//边链表的头指针
};
2.2.3图类的定义
template
classGraph//图的类定义
{
protected:
intmaxVertices;//图中最大的顶点数
intnumEdges;//当前边数
intnumVertices;//当前顶点数
T*output;//存放遍历的数组
T*input;//存放输入数组
Vertex*NodeTable;//顶点表(各边链表的头结点)
intgetVertexPos(constTvertx)//取顶点v在数组中的位置
{
intj=-1;
for(inti=0;i{
if(NodeTable[i].data==vertx)
j=i;
}
returnj;
}
voidDFS(Graph&G,intv,boolvisited[])//图的深度优先搜索
{
cout<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&&iNodeTable[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&G,constT&v);//图的广度优先搜索
voidWheCan(Graph&G);//判断是否为连通图
voidOutPut();//输出
voidHaveEdge(Graph&G);//求顶点的度
voidSerachVertex(Graph&G);//输入顶点x,查找图G:
若存在含x的顶点,则删除该结点及与之相关连的边,并作DFS遍历
voidChangeGraph(Graph&G);//将用邻接表表示的数转换为邻接矩阵表示
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的顶点,则删除该结点及与之相关连的边,并作DFS遍历(执行操作3);否则输出信息“无x”;
输入顶点,在顶点表中所搜是否含有这个顶点,如果没有,就输出“无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
#include
#include"Queue.h"
usingnamespacestd;
template
structEdge//边结点的定义
{
intdest;//边的另一顶点位置
Ecost;//边上的权值
Edge*link;//下一条边链指针
Edge(){}//构造函数
Edge(intnum,Ecost):
dest(num),weight(cost),link(NULL){}//构造函数
booloperator!
=(Edge&R)const//判边等否
{
returndest!
=R.dest;
}
};
template//顶点的定义
structVertex
{
Tdata;//顶点的名字
Edge*adj;//边链表的头指针
};
template
classGraph//图的类定义
{
protected:
intmaxVertices;//图中最大的顶点数
intnumEdges;//当前边数
intnumVertices;//当前顶点数
T*output;//存放遍历的数组
T*input;//存放输入数组
Vertex*NodeTable;//顶点表(各边链表的头结点)
intgetVertexPos(constTvertx)//取顶点v在数组中的位置
{
intj=-1;
for(inti=0;i{
if(NodeTable[i].data==vertx)
j=i;
}
returnj;
}
voidDFS(Graph&G,intv,boolvisited[])//图的深度优先搜索
{
cout<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&&iNodeTable[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&G,constT&v);//图的广度优先搜索
voidWheCan(Graph&G);//判断是否为连通图
voidOutPut();//输出
voidHaveEdge(Graph&G);//求顶点的度
voidSerachVertex(Graph&G);//输入顶点x,查找图G:
若存在含x的顶点,则删除该结点及与之相关连的边,并作DFS遍历
voidChangeGraph(Graph&G);//将用邻接表表示的数转换为邻接矩阵表示
voidInput();//输入
};
#include
template
Graph:
:
Graph()//构造函数:
建立一个空的邻接表
{
maxVertices=100;
numVertices=0;
numEdges=0;
NodeTable=newVertex[maxVertices];//创建顶点表数组
if(NodeTable==NULL)
{
cerr<<"存储分配错!
"<exit
(1);
}
for(inti=0;i{
NodeTable[i].adj=NULL;
}
output=newT[maxVertices];
}
template
Graph:
:
~Graph()//析构函数:
删除一个邻接表
{
for(inti=0;i{
Edge*p=NodeTable[i].adj;
while(p!
=NULL)
{
NodeTable[i].adj=p->link;
deletep;
p=NodeTable[i].adj;
}
}
delete[]NodeTable;//删除顶点表数组
}
template
boolGraph:
:
insertVertex(constT&vertex)//插入顶点
{
if(numVertices==maxVertices)
returnfalse;
NodeTable[numVertices].data=vertex;
numVertices++;
returntrue;
}
template
boolGraph:
:
insertEdge(intv1,intv2,Ecost)//插入边(v1,v2),权值为cost
{
if(v1>=0&&v1<=numVertices&&v2>=0&&v2<=numVertices)
{
Edge*q,*p=NodeTable[v1].adj;//v1对应的边链表头z指针
while(p!
=NULL&&p->dest!
=v2)//寻找邻接顶点v2
p=p->link;
if(p!
=NULL)//找到此边不插入
returnfalse;
p=newEdge;//否则创建新节点
q=newEdge;
p->dest=v2;
p->cost=cost;
p->link=NodeTable[v1].adj;//链入v1的边链表
NodeTable[v1].adj=p;
q->dest=v1;
q->cost=cost;
q->link=NodeTable[v2].adj;//链入v2的边链表
NodeTable[v2].adj=q;
numEdges++;
returntrue;
}
else
{
cerr<<"参数有误!
请重新输入!
"<exit
(1);
returnfalse;
}
}
template
boolGraph:
:
removeVertex(intv)
{
if(numVertices==1||v<0||v>=numVertices)
{
cerr<<"参数有误,请重新输入!
"<exit
(1);
returnfalse;//表空或顶点超出范围
}
Edge*p,*s,*t;
intk;
while(NodeTable[v].adj!
=NULL)
{
p=NodeTable[v].adj;
k=p->dest;
s=NodeTable[k].adj;
t=NULL;
while(s!
=NULL&&s->dest!
=v)
{
t=s;
s=s->link;
}
if(s!
=NULL)
{
if(t==NULL)
NodeTable[k].adj=s->link;
else
t->link=s->link;
deletes;
}
NodeTable[v].adj=p->link;
deletep;
numEdges--;
}
numVertices--;
NodeTable[v].data=NodeTable[numVertices].data;
p=NodeTable[v].adj=NodeTable[numVertices].adj;
while(p!
=NULL)
{
s=NodeTable[p->dest].adj;
while(s!
=NULL)
if(s->dest==numVertices)
{
s->dest=v;
break;
}
else
s=s->link;
}
returntrue;
}
template
boolGraph:
:
removeEdge(intv1,intv2)
{
if(v1!
=-1&&v2!
=-1)
{
Edge*p=NodeTable[v1].adj,*q=NULL,*s=p;
while(p!
=NULL&&p->des