图的定义和基本术语精.docx
《图的定义和基本术语精.docx》由会员分享,可在线阅读,更多相关《图的定义和基本术语精.docx(19页珍藏版)》请在冰豆网上搜索。
图的定义和基本术语精
目录
第6章图2
6.1图的定义和基本术语2
6.2图的存储和创建3
6.2.1图的存储表示3
6.2.2图的创建6
6.3图的遍历6
6.3.1深度优先搜索6
6.3.2广度优先搜索7
6.4遍历算法的应用8
6.4.1应用问题概述8
6.4.2求一条包含图中所有顶点的简单路径9
6.4.3求距v0最短路径长度最长的一个顶点10
6.5图的连通性问题11
6.5.1无向图的连通分量和生成树11
6.5.2最小生成树13
6.6有向无环图及其应用13
第6章图
6.1图的定义和基本术语
1、图的特征
任意两个数据元素之间都可能相关。
结点之间的关系是多对多的。
G=(V,{E})
2、基本术语
结点:
顶点
结点间的关系:
无向图:
边(v,w),v与w互为邻接点,边(v,w)依附于顶点v,w,边(v,w)和顶点v,w相关联
v的度:
和v相关联的边的数目。
有向图:
弧,v弧尾,w弧头,顶点v邻接到顶点w,顶点w邻接自顶点v,弧和顶点v,w相关联。
v的入度:
以v为头的弧的数目;v的出度:
以v为尾的弧的数目;
v的度:
v的入度与出度之和。
路径、回路(环)、简单路径、简单回路(简单环)
连通性:
若从顶点v到顶点v’有路径,则称v和v’是连通的
图的规模:
顶点数n、边(弧)数e、顶点的度(有向图:
入度/出度)
子图:
G’=(V’,{E’}),G=(V,{E}),若V’V且E’E,则称G’是G的子图。
图的分类:
1)关系的方向性(无向/有向)、关系上是否有附加的数——权(图/网)
有向图、无向图、有向网、无向网
2)边(弧)数:
完全图(边数=n(n-1)/2的无向图)、有向完全图(弧数=n(n-1)的有向图)
稀疏图(enlogn)
3)连通性:
无向图:
连通图(任意两顶点都是连通的)、连通分量(极大连通子图)、生成树(极小连通子图)、生成森林
有向图:
强/弱连通图、强连通分量、生成树(极小连通子图)、生成森林
3、抽象数据类型定义
ADTGraph{
数据对象V:
V是具有相同特性的数据元素的集合,称为顶点集。
数据关系R:
R={VR}
VR={|v,wV且P(v,w),表示从v到w的弧,谓词P(v,w)定义了弧的意义或信息}
基本操作:
CreateGraph(&G,V,VR)
初始条件:
V是图的顶点集,VR是图中弧的集合
操作结果:
按V和VR的定义构造图G
DestroyGraph(&G)
初始条件:
图G存在
操作结果:
销毁图G
LocateVex(G,u)
初始条件:
图G已存在,u和G中顶点有相同特征
操作结果:
若G中存在顶点u,则返回该顶点在图中位置,否则返回其它信息
GetVex(G,v)
初始条件:
图G存在,v是G中某个顶点
操作结果:
返回v的值
PutVex(&G,v,value)
初始条件:
图G存在,v是G中某个顶点
操作结果:
对v赋值value
FirstAdjVex(G,v)
初始条件:
图G存在,v是G中某个顶点
操作结果:
返回v的第一个邻接顶点。
若顶点在G中没有邻接顶点,则返回“空”
NextAdjVex(G,v,w)
初始条件:
图G存在,v是G中某个顶点,w是v的邻接顶点
操作结果:
返回v的(相对于w的)下一个邻接顶点。
若w是v的最后一个邻接点,则返回“空”
InsertVex(&G,v)
初始条件:
图G存在,v和G中顶点有相同特征
操作结果:
在图中增添新顶点v
DeleteVex(&G,v)
初始条件:
图G存在,v是G中某个顶点
操作结果:
删除G中顶点v及其相关的弧
InsertArc(&G,v,w)
初始条件:
图G存在,v和w是G中两个顶点
操作结果:
在图G中增添弧,若G是无向的,则还应增添对称弧
DeleteArc(&G,v,w)
初始条件:
图G存在,v和w是G中两个顶点
操作结果:
删除G中的弧,若G是无向的,则还应删除对称弧
DFSTraverse(G,v,visit())
初始条件:
图G存在,v是G中某个顶点,visit是对顶点的应用函数
操作结果:
从顶点v起深度优先遍历图G,并对每个顶点调用函数visit()一次且至多一次。
一旦visit()失败,则操作失败
BFSTraverse(G,v,visit())
初始条件:
图G存在,v是G中某个顶点,visit是对顶点的应用函数
操作结果:
从顶点v起广度优先遍历图G,并对每个顶点调用函数visit()一次且至多一次。
一旦visit()失败,则操作失败
}ADTGraph
6.2图的存储和创建
6.2.1图的存储表示
1、图的存储表示分析
∵顶点之间的关系是多对多的(m:
n),由于m和n都是不定的,无法给出一个这种多对多的关系向线性关系的映射公式
∴图中的关系不能通过顺序映像(即通过顶点之间的存储位置反映顶点之间的逻辑关系)反映;必须另外引入存储空间反映顶点之间的邻接关系。
图的存储结构:
1)顶点信息;2)边(弧)信息;3)整体信息:
顶点数、边(弧)数、图的种类(有向图、无向图、有向网、无向网)
顶点集的存储:
∵图的应用中,顶点集动态变化的几率十分小
∴顶点集可以采用顺序表存储,按预先估计的最大顶点数分配空间
(顺序表和链表:
若数据元素集是静态的,采用顺序表要好(随机存取);若数据元素集是动态的,则采用链表要好(动态分配与释放))
#defineMAX_VERTEX_NUM20/*最大顶点数*/
注意:
顺序表与顺序映像之间的区别
关系集的存储:
在顶点确定的情况下,边或弧的数目也是不定的,且在实际应用中,可能会改变图中的顶点之间的关系。
邻接矩阵表示法:
矩阵中的第i行第j列的元素反映图中第i个顶点到第j个顶点是否存在弧,若存在其附加的信息是什么。
邻接表表示法:
将每一顶点的邻接点位置串成一个链,称为邻接表。
对于有向图/网来说,该邻接表反映的是顶点的出边表。
typedefenum{DG,DN,AG,AN}GraphKind;/*{有向图,有向网,无向图,无向网}*/
2、邻接矩阵表示法(数组表示法)
无向图/网:
对称矩阵有向图/网:
非对称矩阵
图:
邻接关系用1/0表示
网:
邻接关系需要进一步反映权值,用INFINITY表示无穷大,反映顶点之间无邻接关系
#defineINT_MAX32767/*最大整数*/
#defineINFINITYINT_MAX
1)邻接矩阵
typedefstructArcCell{
intadj;/*顶点间关系,无权图:
0-不相邻,1-相邻
有权图,权值,INFINITY-不相邻
*/
InfoType*info;/*该弧相关信息的指针*/
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
2)图的整体结构
typedefstruct{
charvexs[MAX_VERTEX_NUM];/*有效的顶点下标从0开始*/
AdjMatrixarcs;/*关系集*/
intvexnum,arcnum;/*顶点数、边/弧数*/
GraphKindkind;/*图的种类*/
}MGraph;
3、邻接表表示法
无向图/网:
边表,表结点的个数为边数的两倍
有向图/网:
出边表,表结点的个数为弧数
1)邻接表的表结点
typedefstructArcNode{
intadjvex;/*弧所指向的顶点的位置*/
structArcNode*nextarc;/*指向下一条弧的指针*/
InfoType*info;
}ArcNode;
2)邻接表的头结点
typedefstructVNode{
chardata;/*顶点信息*/
ArcNode*firstarc;/* 邻接表指针*/
}VNode,AdjList[MAX_VERTEX_NUM];
3)图的整体结构
typedefstruct{
AdjListvertices;
intvexnum,arcnum;
GraphKindkind;
}ALGraph;
4、邻接矩阵与邻接表的对比
假设图为G,顶点数为n,边/弧数为e。
A邻接矩阵
B邻接表
存储空间
O(n+n2)
O(n+e)
图的创建算法
T1(n)=O(e+n2)或T2(n)=O(e*n+n2)
T1(n)=O(n+e)或T2(n)=O(e*n)
T1(n)是指在输入边/弧时,输入的顶点信息为顶点的编号;而T2(n)则指在输入边/弧时,输入的为顶点本身的信息,此时需要查找顶点在图中的位置
无向图中求第i顶点的度
(第i行之和)或
(第i列之和)
G.vertices[i].firstarc所指向的邻接表包含的结点个数
无向网中求第i顶点的度
第i行/列中adj值不为INFINITY的元素个数
有向图中求第i顶点的入/出度
入度:
(第i列)
出度:
(第i行)
入度:
扫描各顶点的邻接表,统计表结点的adjvex为i的表结点个数T(n)=O(n+e)
出度:
G.vertices[i].firstarc所指向的邻接表包含的结点个数
有向网中求第i顶点的入/出度
入度:
第i列中adj值不为INFINITY的元素个数
出度:
第i行中adj值不为INFINITY的元素个数
统计边/弧数
无向图:
)
无向网:
G.arcs中adj值不为INFINITY的元素个数的一半
有向图:
有向网:
G.arcs中adj值不为INFINITY的元素个数
无向图/网:
图中表结点数目的一半
有向图/网:
图中表结点的数目
结论:
邻接矩阵适于稠密图,邻接表适于稀疏图;邻接表求有向图的顶点的入度不方便,要遍历全部的邻接表。
6.2.2图的创建
基本过程:
1)输入图的类型,根据类型选择相应的创建算法
2)输入图的顶点数,边/弧数
3)输入并存储顶点信息
4)输入边/弧所关联的顶点对,将边或弧的信息存储到邻接矩阵/邻接表中
图的存储结构不同、图的类型不同,都会影响创建算法的实现细节;但是,图的总体创建流程是一致的(如上)。
示例:
用邻接矩阵表示法构造有向网G
StatusCreateMDG(MGraph&G){
/*步骤2:
输入图的顶点数、边/弧数*/
scanf(&G.vexnum,&G.arcnum,&IncInfo);/*IncInfo为0则各弧不含其它信息*/
/*步骤3:
输入并存储顶点信息*/
for(i=0;i/*步骤4:
输入并存储边/弧信息*/
for(i=0;ifor(j=0;jG.arcs[i][j]={INFINITY,NULL};
for(k=0;kscanf(&v1,&v2,&w);
i=LocateVex(G,v1);j=LocateVex(G,v2);
G.arcs[i][j].adj=w;
if(IncInfo)Input(*G.arcs[i][j].info);/**G.arcs[i][j].info要求G.arcs[i][j].info指向的空间在调用Input()前分配*/
}
}
6.3图的遍历
6.3.1深度优先搜索
1、分析
·类似于树的先序遍历
·引入访问标志数组visit[0:
n-1],区分顶点是否已被访问
·非连通图,需引入多个深度优先搜索的起始顶点
·递归算法或基于栈的非递归算法
2、基于逻辑结构的算法
Booleanvisited[MAX_VERTEX_NUM];
voidDFSTraverse(GraphG)
{
for(v=0;vfor(v=0;vif(!
visited[v])DFS(G,v);
}
voidDFS(GraphG,intv)
{
visited[v]=TRUE;Visit(G,v);
for(w=FirstAdjVex(G,v);w;w=NextAdjVex(G,v,w))
if(!
visited[w])DFS(G,w);
}
3、基于某种存储结构的算法
根据选择的存储结构,决定FirstAdjVex()和NextAdjVex()的实现,重新整合算法。
如采用邻接矩阵表示法表示的图,则DFS算法如下:
voidDFS(MGraphG,intv)
{
visited[v]=TRUE;Visit(G,v);
for(w=0;wif(G.arcs[v][w].adj&&!
visited[w])DFS(G,w);
}
如采用邻接表表示法表示的图,则DFS算法如下:
voidDFS(ALGraphG,intv)
{
visited[v]=TRUE;Visit(G,v);
for(p=G.vertices[v].firstarc;p;p=p->nextarc)
if(!
visited[p->adjvex])DFS(G,p->adjvex);
}
6.3.2广度优先搜索
1、分析
·类似于树的层次遍历
·引入visited访问标志数组
·非连通图,需引入多个广度优先搜索的起始顶点
·引入队列保存“顶点已访问,但其邻接点未全访问”的顶点编号
2、基于逻辑结构的算法
voidBFSTraverse(GraphG)
{
for(v=0;vInitQueue(Q);
for(v=0;vif(!
visited[v]){
/*访问某连通分量的起始顶点,起点入队*/
visited[v]=TRUE;Visit(G,v);
EnQueue(Q,v);
while(!
QueueEmpty(Q)){
/*出队,访问出队元素的邻接点*/
DeQueue(Q,u);
for(w=FirstAdjVex(G,u);w;w=NextAdjVex(G,u,w))
if(!
visited[w]){
/*访问顶点u的尚未访问的邻接点并入队*/
visited[w]=TRUE;Visit(G,w);
EnQueue(Q,w);
}
}
}
}
3、基于某种存储结构的算法
如采用邻接矩阵表示法表示的网,则算法如下:
voidBFSTraverse(MGraphG)
{
for(v=0;vInitQueue(Q);
for(v=0;vif(!
visited[v]){
/*访问某连通分量的起始顶点,起点入队*/
visited[v]=TRUE;Visit(G,v);
EnQueue(Q,v);
while(!
QueueEmpty(Q)){
/*出队,访问出队元素的邻接点*/
DeQueue(Q,u);
for(w=0;wif(G.arcs[u][w].adj!
=INFINITY&&!
visited[w]){
/*访问顶点u的尚未访问的邻接点并入队*/
visited[w]=TRUE;Visit(G,w);
EnQueue(Q,w);
}
}
}
}
如采用邻接表表示法表示的网,则算法如下:
voidBFSTraverse(ALGraphG)
{
for(v=0;vInitQueue(Q);
for(v=0;vif(!
visited[v]){
/*访问某连通分量的起始顶点,起点入队*/
visited[v]=TRUE;Visit(G,v);
EnQueue(Q,v);
while(!
QueueEmpty(Q)){
/*出队,访问出队元素的邻接点*/
DeQueue(Q,u);
for(p=G.vertices[u].firstarc;p;p=p->nextarc)
if(!
visited[p->adjvex]){
/*访问顶点u的尚未访问的邻接点并入队*/
visited[p->adjvex]=TRUE;Visit(G,p->adjvex);
EnQueue(Q,p->adjvex);
}
}
}
}
6.4遍历算法的应用
6.4.1应用问题概述
图的深度优先遍历:
1、求一条包含图中所有顶点的简单路径(简单回路)
2、判断图中是否存在环
3、求图中通过给定顶点vk的简单回路
4、判断是否存在从顶点vi到顶点vj的的路径
图的广度优先遍历:
1、判断是否存在从顶点vi到顶点vj的的路径
2、求距v0最短路径长度最长的一个顶点。
3、求v0和v1之间的最短路径.
4、在顶点子集U中找出距离顶点v0最近的顶点
5、求顶点v0到其余每个顶点的最短路径
6、求距离顶点v0的最短路径长度为k的所有顶点
7、判别v0和v1之间是否存在一条长度为k的路径(也可以用深度优先搜索算法,结合回溯法)
6.4.2求一条包含图中所有顶点的简单路径
1、思路
对于任意的有向图或无向图G,并不一定都能找到符合题意的简单路径。
这样的简单路径要求包含G.vexnum个顶点,且互不相同。
它的查找可以基于深度优先遍历。
在一个存在包含全部顶点的简单路径的图中,以下因素会影响该简单路径是否能顺利地查到:
1)起点的选择:
如图(a),其符合题意的一条简单路径如图(b)。
若起点为1,则不能找到符合题意的简单路径;
2)顶点的邻接点次序:
进一步考察图(a),即使以2为起点,但是2的邻接点选择的是1,而不是5,此时也不能找到符合题意的解。
在基于DFS的查找算法中,由于起点和邻接点的选取是与顶点和邻接点的存储次序以及算法的搜索次序有关,不可能依据特定的图给出特定的解决算法。
因此,在整个搜索中应允许有查找失败,此时可采取回溯到上一层的方法,继续查找其他路径。
这样,引入数组Path用来保存当前已搜索的简单路径上的顶点,引入计数器n用来记录当前该路径上的顶点数。
对DFS算法的修改如下:
1)计数器n的初始化,放在visited的初始化前后;
2)访问顶点时,增加将该顶点序号入数组Path中,计数器n++;判断是否已获得所求路径,是则输出结束,否则继续遍历邻接点;
3)某顶点的全部邻接点都访问后,仍未得到简单路径,则回溯,将该顶点置为未访问,计数器n--。
2、算法
/*邻接矩阵表示法,粗体字部分为在深度优先遍历上的增加或修改的步骤*/
voidHamilton(MGraphG)
{
for(i=0;ivisited[i]=FALSE;
n=0;
for(i=0;iif(!
visited[i])DFS(G,i);
}
voidDFS(MGraphG,inti)
{
visited[i]=TRUE;
Path[n]=i;
n++;
if(n==G.vexnum)Print(Path);/*符合条件,输出该简单路径*/
for(j=0;jif(G.arcs[i][j].adj&&!
visited[j])
DFS(G,j);
visited[i]=FALSE;
n--;
}
3、思考
1)若图中存在多条符合条件的路径,本算法是输出一条,还是输出全部?
2)如何修改算法,变成判断是否有包含全部顶点的简单路径?
3)如何修改算法,输出包含全部顶点的简单路径的条数?
4)如何修改算法,输出所有的简单回路?
5)考虑其他图的存储方法对算法的影响;
6)考虑在非递归的深度优先遍历算法上,如何修改使之适应本问题的求解。
6.4.3求距v0最短路径长度最长的一个顶点
1、思路
由于题意强调为最短路径,因此应当考虑BFS的算法应用。
本问题的求解转变成:
从v0出发进行BFS,最后一层的顶点距离v0的最短路径长度最长。
由于BFS类似于树的按层次遍历,需要引入队列用来保存本身已访问但其邻接点尚未全部访问的顶点。
BFS遍历中最后一层的顶点一定是最后出队的若干顶点,队列中最后一个出队的顶点必定是符合题意的顶点。
这样,只需调用BFS的算法,将最后出队的元素返回即可。
2、算法
intMaxDistance(MGraphG,intv0)
{
/*初始化各顶点的访问标志,设置为未访问*/
for(i=0;ivisited[i]=FALSE;
InitQueue(Q);
/*不需要考虑其他的连通分量,因为所求的顶点必定与v0在同一个连通分量中*/
EnQueue(Q,v0);
visited[v0]=TRUE;
while(!
QueueEmpty(Q))
{
DeQueue(Q,v);
for(w=0;wif(G.arcs[v][w].adj&&!
visited[w])
{
visited[w]=TRUE;
EnQueue(Q,w);
}
}
return(v);
}
3、思考
1)若要求输出满足条件的全部顶点,则如何修改上述算法;
2)考虑其它图的存储表示下的算法实现。
4、其它类似的应用
·求v0和v1之间的最短路径;
·在顶点子集U中找出距离顶点v0最近的顶点;
·求顶点v0到其余每个顶点的最短路径;
·求距离顶点v0的最短路径长度为K的所有顶点;
·判别v0和v1之间是否存在长度为K的路径。
6.5图的连通性问题
6.5.1