7.1 图-数据类型及存储表示

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,wVP(v,w)<v,w>vwP(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;
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; 
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;

在邻接多重表中,所有依附于同一顶点的边串联在同一链表中,由于每条边依附于两个顶点,则每个边结点同时链接在两个链表中。可见,对无向图而言,其邻接多重表和邻接表的差别,仅仅在于同一条边在邻接表中用两个结点表示,而在邻接多重表中只有一个结点。因此,除了在边结点中增加一个标志域外,邻接多重表所需的存储量和邻接表相同。在邻接多重表上,各种基本操作的实现亦和邻接表相似。

附:常用名词和术语

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 VV , V R ′ ⊆ V R VR^{\prime} \subseteq VR VRVR ,则称 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 VV E ′ ⊆ E E'\subseteq E EE,则称 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(n1)/2 条边的无向图称作完全图
含有 e = n ( n − 1 ) e=n(n-1) e=n(n1) 条弧的有向图称作有向完全图

若边或弧的个数 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,j1, vi,j)VR; 1jm,则称从顶点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,j1, vi,j>VR; 1jm,则路径也是有向的。

路径长度是路径上的边或弧的数目。

第一个顶点和最后一个顶点相同的路径称为回路(Cycle)。

若序列中的顶点不重复出现的路径称作简单路径

除了第一个顶点和最后一个顶点之外(u=w),其余顶点不重复出现的回路,称为简单回路简单环

7 连通图、连通分量、强连通图、强连通分量

若图G中任意两个顶点之间都有路径相通,则称此图为连通图;
若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。

对有向图,若任意两个顶点之间都存在一条有向路径,则称此有向图为强连通图。
否则,其各个强连通子图称作它的强连通分量。

极小连通子图极大连通子图是在 无向图 中进行讨论的。
极大强连通子图是在 有向图 中进行讨论的,不存在极小强连通子图

7.1 在无向图中

如果从顶点v到顶点v’有路径,则称v和v'是连通的。
如果对于图中任意两个顶点 v i , v j ∈ V v_{i},v_{j} \in V vi,vjV 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,vjV 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 条边。

如果在一棵生成树上添加一条边,必定构成一个环,因为这条边使得它依附的那两个顶点之间有了第二条路径。

一棵有 n 个顶点的生成树有且仅有 n-1 条边。
如果一个图有 n 个顶点和小于 n-1 条边,则是非连通图。
如果它多于 n-1 条边,则一定有环;但有 n-1 条边的图不一定是生成树。

如果一个有向图恰有一个顶点的入度为0,其余顶点的入度均为1,则是一棵有向树。
一个有向图的生成森林由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。

假设一个连通图有 n 个顶点和 e 条边,其中 n-1 条边和 n 个顶点构成一个极小连通子图,称该极小连通子图为此连通图的生成树。

对非连通图,则称由各个连通分量的生成树的集合为此非连通图的生成森林。



——《数据结构图 (C语言版) 严蔚敏》 学习笔记

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值