图-数据类型及基本操作
1 抽象数据类型图的定义
ADT Graph{
数据对象V:
V 是 具 有 相 同 特 性 的 数 据 元 素 的 集 合 , 称 为 顶 点 集 。 \qquad V是具有相同特性的数据元素的集合,称为顶点集。 V是具有相同特性的数据元素的集合,称为顶点集。
数据关系R:
R = { V R } \qquad R=\{VR\} R={VR}
V R = { < v , w > ∣ v , w ∈ V , 且 P ( v , w ) , < v , w > 表 示 从 v 到 w 的 弧 , 谓 词 P ( v , w ) 定 义 了 弧 < v , w > 的 意 义 或 信 息 ; } \qquad VR=\{<v,w>|v,w \in V, 且P(v,w),<v,w>表示从v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息;\} VR={<v,w>∣v,w∈V,且P(v,w),<v,w>表示从v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息;}
基本操作P:
结构的建立和销毁
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中增添弧<v,w>;若G是无向的,则还增添对称弧<w,v>。
DeleteArc(&G, v, w);
初始条件:图G存在,v和w是G中两个顶点。
操作结果:在G中删除弧<v,w> ;若G是无向的,则还删除对称弧<w,v>。
遍历
DFSTraverse(G, Visit());
初始条件:图G存在,Vvisit是顶点的应用函数。
操作结果:对图进行深度优先遍历。在遍历过程中对每个顶点调用函数visit一次且仅一次。一旦visit()失败,则操作失败。
BFSTraverse(G, Visit());
初始条件:图G存在,Visit是顶点的应用函数。
操作结果:对图进行广度优先遍历。在遍历过程中对每个顶点调用函数Visit一次且仅一次。一旦visit()失败,则操作失败。
}ADT Graph
2 图的存储表示
2.1 图的数组(邻接矩阵)存储表示
用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。
//----图的数组(邻接矩阵)存储表示----
#define INFINITY INT_MAX //最大值$\infty$
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef enum { DG, DN, UDG, UDN } GraphKind; //{有向图,有向网,无向图,无向网}
typedef struct ArcCell{
VRType adj; //VRType是顶点关系类型。
//对无权图,用1或0表示相邻否。
//对带权图,则为权值类型。
InfoType *info; //该弧相关信息的指针。
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct{
VertexType vexs[MAX_VERTEX_NUM]; //顶点向量
AdjMatrix arcs; //邻接矩阵
int vexnum, arcnum; //图的当前顶点数和弧数
GraphKind kind; //图的种类标志
}MGraph;
![](https://i-blog.csdnimg.cn/blog_migrate/45ab9a0fd0ba773b8835f7ce97a3e2f0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d023670b487bb351fb5778c1f80a2ef8.png)
Status CreateGraph(MGraph &G){
//采用数组(邻接矩阵)表示法,构造图G.
scanf(&G.kind);
switch(G.kind){
case DG: return CreateDG(G); //构造有向图G
case DN: return CreateDN(G); //构造有向网G
case UDG: return CreateUDG(G); //构造无向图G
case UDN: return CreateUDN(G); //构造无向网G
default: return ERROR;
}
}// CreateGraph
Status CreateUDN(MGraph &G){
//采用数组(邻接矩阵)表示法,构造无向网G
scanf(&G.vexnun, &G.arcnum, &IncInfo); // IncInfo为0则各弧不含其他信息
for(i=0; i<G.vexnum; ++i){
scanf(&G.vexs[i]); //构造顶点向量
}
for(i=0; i<G.vexnum; ++i){
for(j=0; j<G.vexnum; ++j){ //初始化邻接矩阵
G.arcs[i][j] = {INEINITY, NULL}; //{adj, info}
}
}
for(k=0; k<G.arcnum; ++k){ //构造邻接矩阵
scanf(&vl, &v2, &w); //输入一条边依附的顶点及权值
i = LocateVex(G, vl);
j = LocateVex(G, v2); //确定v1和v2在G中位置
G.arcs[i][j].adj = w; //弧<vl,v2>的权值
if(IncInfo){
Input(*G.arcs[i][j].info); //若弧含有相关信息,则输入
}
G.arcs[j][i] = G.arcs[i][j];//置<v1,v2>的对称弧<v2,v1>
}
return OK;
}//CreateUDN
2.2 图的邻接表存储表示
图的邻接表(Adjacency List)是图的一种链式存储结构。
//----图的邻接表存储表示----
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef struct ArcNode{
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *nextarc; //指向下一条弧的指针
InfoType *info; //该弧相关信息的指针
}ArcNode;
typedef struct VNode{
VertexType data; //顶点信息
ArcNode *firstarc; //指向第一条依附该顶点的弧
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct{
AdjList vertices;
int vexnum, arcnum;
int kind;//图的种类标志
}ALGraph;
结点类型图例
邻接表存储表示图的图例
2.3 有向图的十字链表存储表示
十字链表(Orthogonal List)是有向图
的另一种链式存储结构。
//----有向图的十字链表存储表示----
#define MAX_VERTEX_NUM 20
typedef struct ArcBox{
int tailvex, headvex; //该弧的尾和头顶点的位置
struct ArcBox *hlink, *tlink; //分别为弧头相同和弧尾相同的弧的链域
InfoType *info; //该弧相关信息的指针
}ArcBox;
typedef struct VexNode{
VertexType data;
ArcBox *firstin, *firstout; //分别指向该顶点第一条入弧和出弧
}VexNode;
typedef struct{
VexNode xlist[MAX_VERTEX_NUM]; //表头向量
int vexnum, arcnum;//有向图的当前顶点数和弧数
}OLGraph;
![](https://i-blog.csdnimg.cn/blog_migrate/29b0962eb9dc8df62268013f403f8997.png)
Status CreateDG(OLGraph &G){
//采用十字链表存储表示,构造有向图G(G.kind =DG)
scanf(&G.vexnum, &G.arcnum, &IncInfo);// IncInfo为0则各弧不含其他信息
for(i=0; i<G.vexnum; ++i){ //构造表头向量
scanf(&G.xlist[i].data); //输人顶点值
G.xlist[i].firstin = NULL; //初始化指针
G.xlist[i].firstout = NULL;
}
for(k=0; k<G.arcnum; ++k){ //输入各弧并构造十字链表
scanf(&v1, &v2); //输入一条弧的始点和终点
i = LocateVex(G, v1); //确定v1和v2在G中位置
j = LocateVex(G, v2);
p = (ArcBox *)malloc(sizeof(ArcBox)); //假定有足够空间
*p = {i, j, G.xlist[j].firstin, G.xlist[i].firstout, NULL}
//对弧结点赋值 {tailvex, headvex, hlink, tlink, info}
G.xlist[j].firstin = G.xlist[i].firstout = p;
//完成在入弧和出弧链头的插人
if(IncInfo){ //若弧含有相关信息,则输入
Input(*p->info);
}
}
}//CreateDG
2.4 无向图的邻接多重表存储表示
邻接多重表(Adjacency Multilist)是无向图
的另一种链式存储结构。
//-----无向图的邻接多重表存储表示-----
#define MAX_VERTEX_NUM 20
typedef emnu { unvisited, visited } VisitIf;
typedef struct EBox{
VisitIf mark; //访问标记
int ivex, jvex; //该边依附的两个顶点的位置
struct EBox *ilink, *jlink; //分别指向依附这两个顶点的下一条边
InfoType *info; //该边信息指针
}EBox;
typedef struct VexBox{
VertexType data;
EBox *firstedge;//指向第一条依附该顶点的边
}VexBox;
typedef struct{
VexBox adjmulist[MAX_VERTEX_NUM];
int vexnum, edgenum; //无向图的当前顶点数和边数
}AMLGraph;
在邻接多重表中,所有依附于同一顶点的边串联在同一链表中,由于每条边依附于两个顶点,则每个边结点同时链接在两个链表中。可见,对无向图而言,其邻接多重表和邻接表的差别,仅仅在于同一条边在邻接表中用两个结点表示,而在邻接多重表中只有一个结点。因此,除了在边结点中增加一个标志域外,邻接多重表所需的存储量和邻接表相同。在邻接多重表上,各种基本操作的实现亦和邻接表相似。
![](https://i-blog.csdnimg.cn/blog_migrate/8b9d53f46b838cdc74678ae4cc27eac1.png)
附:常用名词和术语
1 孤头、弧尾、弧、有向图:
若
<
v
,
w
>
∈
V
R
<v, w> \in VR
<v,w>∈VR,则
<
v
,
w
>
<v, w>
<v,w> 表示从顶点
v
v
v 到顶点
w
w
w 的一条弧。称顶点
v
v
v 为弧尾,称顶点
w
w
w 为弧头。
由顶点集和弧集构成的图称作有向图。
2 边、无向图
若
<
v
,
w
>
∈
V
R
<v, w> \in VR
<v,w>∈VR 必有
<
w
,
v
>
∈
V
R
<w, v> \in VR
<w,v>∈VR ,则称
(
v
,
w
)
(v, w)
(v,w) 为顶点
v
v
v 和顶点
w
w
w 之间存在一条边。
由顶点集和边集构成的图称作无向图
。
3 网、子图
弧或边带权的图分别称作有向网
或无向网
。
设图
G
=
(
V
,
{
V
R
}
)
G=(V,\{VR\})
G=(V,{VR}) 和图
G
′
=
(
V
′
,
{
V
R
′
}
)
G^{\prime}=(V^{\prime},\{VR^{\prime}\})
G′=(V′,{VR′}) ,且
V
′
⊆
V
V^{\prime} \subseteq V
V′⊆V ,
V
R
′
⊆
V
R
VR^{\prime} \subseteq VR
VR′⊆VR ,则称
G
′
G^{\prime}
G′ 为
G
G
G 的子图
。
假设有两个图 G = ( V , { E } ) G=(V, \{E\}) G=(V,{E}) 和 G ′ = ( V ′ , { E ′ } ) G'=(V', \{E'\}) G′=(V′,{E′}) ,如果 V ′ ⊆ V V'\subseteq V V′⊆V且 E ′ ⊆ E E'\subseteq E E′⊆E,则称 G ′ G' G′ 为 G G G 的
子图
(Subgraph)。
4 完全图、稀疏图、稠密图
假设图中有
n
n
n 个顶点,
e
e
e 条,则
含有
e
=
n
(
n
−
1
)
/
2
e=n(n-1)/2
e=n(n−1)/2 条边的无向图称作完全图
;
含有
e
=
n
(
n
−
1
)
e=n(n-1)
e=n(n−1) 条弧的有向图称作有向完全图
;
若边或弧的个数
e
<
n
l
o
g
2
n
e<nlog_{2}{n}
e<nlog2n ,则称作稀疏图
,
否则称作稠密图
。
5 邻接点、度、入度、出度
假若顶点v和顶点w之间存在一条边,则称顶点v和w互为邻接点,边(v, w)和顶点v和w相关联。
和顶点v关联的边的数目定义为边的度。
对有向图来说,
以顶点v为弧尾的弧的数目定义为顶点的出度;以顶点v为孤头的弧的数目定义为顶点的入度。
度(TD
) = 出度(OD
) + 入度(ID
)
6 路径、路径长度、简单路径、简单回路
设图 G = ( V , { V R } ) G=(V,\ \{VR\}) G=(V, {VR}) 中从顶点 u u u 到顶点 w w w 的路径(Path)的一个顶点序列 { u = v i , 0 , v i , 1 , . . . , v i , m = w } \{u=v_{i,0},\ v_{i,1},\ ...,\ v_{i,m}=w\} {u=vi,0, vi,1, ..., vi,m=w} :
若图G为无向图: ( v i , j − 1 , v i , j ) ∈ V R ; 1 ⩽ j ⩽ m (v_{i,j-1},\ v_{i,j}) \in VR ;\ 1\leqslant j\leqslant m (vi,j−1, vi,j)∈VR; 1⩽j⩽m,则称从顶点u到顶点w之间存在一条路径,路径上边的数目称作
路径长度
。
若图G为有向图: < v i , j − 1 , v i , j > ∈ V R ; 1 ⩽ j ⩽ m <v_{i,j-1},\ v_{i,j}> \in VR ;\ 1\leqslant j\leqslant m <vi,j−1, vi,j>∈VR; 1⩽j⩽m,则路径也是有向的。
路径长度
是路径上的边或弧的数目。
第一个顶点和最后一个顶点相同的路径称为回路
或环
(Cycle)。
若序列中的顶点不重复出现的路径称作简单路径
。
除了第一个顶点和最后一个顶点之外(u=w),其余顶点不重复出现的回路,称为简单回路
或简单环
。
7 连通图、连通分量、强连通图、强连通分量
若图G中任意两个顶点之间都有路径相通,则称此图为连通图;
若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。
对有向图,若任意两个顶点之间都存在一条有向路径,则称此有向图为强连通图。
否则,其各个强连通子图称作它的强连通分量。
极小连通子图
与极大连通子图
是在 无向图 中进行讨论的。
极大强连通子图
是在 有向图 中进行讨论的,不存在极小强连通子图
。
7.1 在无向图中
如果从顶点v到顶点v’有路径,则称v和v'是连通
的。
如果对于图中任意两个顶点
v
i
,
v
j
∈
V
v_{i},v_{j} \in V
vi,vj∈V ,
v
i
v_{i}
vi 和
v
j
v_{j}
vj 都是连通的,则称G是连通图
(Connected Graph)。
连通分量
(Connected Component)指的是无向图中的极大连通子图
。
7.2 在有向图中
如果对于每一对
v
i
,
v
j
∈
V
v_{i},v_{j} \in V
vi,vj∈V ,
v
i
≠
v
j
v_{i} \not= v_{j}
vi=vj ,从
v
i
v_{i}
vi 到
v
j
v_{j}
vj 和从
v
j
v_{j}
vj 到
v
i
v_{i}
vi 都存在有向路径,则称G是强连通图
。
有向图中的极大强连通子图称做有向图的强连通分量
。
8 生成树、生成森林
一个连通图的生成树
是一个极小连通子图,它含有图中全部顶点,但只有足以构成一棵树的 n-1 条边。
如果在一棵生成树上添加一条边,必定构成一个环,因为这条边使得它依附的那两个顶点之间有了第二条路径。
![](https://i-blog.csdnimg.cn/blog_migrate/20863ad77132e44800e40b6a7a113de5.png)
一棵有 n 个顶点的生成树有且仅有 n-1 条边。
如果一个图有 n 个顶点和小于 n-1 条边,则是非连通图。
如果它多于 n-1 条边,则一定有环;但有 n-1 条边的图不一定是生成树。
如果一个有向图恰有一个顶点的入度为0,其余顶点的入度均为1,则是一棵有向树。
一个有向图的生成森林由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。
假设一个连通图有 n 个顶点和 e 条边,其中 n-1 条边和 n 个顶点构成一个极小连通子图,称该极小连通子图为此连通图的生成树。
对非连通图,则称由各个连通分量的生成树的集合为此非连通图的生成森林。
——《数据结构图 (C语言版) 严蔚敏》 学习笔记