图的定义与基本术语
图的定义
图(Graph)是一种网状数据结构。
形式化定义如下:
Graph=(V,R)
V={x∣x ∈ DataObject}
R={VR}
VR={<x,y>∣P(x,y)∧(x,y∈V)}
DataObject为具有相同特性元素的集合。
V中的数据元素通常称为顶点;
VR是两个顶点之间的关系的集合;
P(x,y)表示x和y之间有特定的关联属性P;
若<x,y>∈VR,则<x,y>表示从顶点x到顶点y的一条弧;
并称x为弧尾或起始点,称y为弧头或终端点。
若图中的边是有方向的,称这样的图为有向图;
若<x,y>∈VR,必有<y,x>∈VR,即VR是对称关系,这时以无序对(x,y)来代替两个有序对,表示x和y之间的一条边,此时的图称为无向图。
顶点位置:
可以将任一顶点看成是图的第一个顶点,同理,对于任一顶点而言,它的邻接点之间也不存在顺序关系。顶点在这个人为的随意排列中的位置序号称为顶点在图中的位置。
基本术语
1、完全图,稀疏图与稠密图
设用n表示图中顶点的个数,用 e表示图中边或弧的数目,不考虑图中每个顶点到其自身的边或弧。
无向完全图:
图中每个顶点和其余n-1个顶点都有边相连(边数:n(n-1)/2)的无向图。
有向完全图:
图中每个顶点和其余n-1个顶点都有弧相连(弧数:n(n-1))的有向图。
稀疏图:
对于有很少条边的图(e < n log n)称为稀疏图,反之称为稠密图。
2、子图:
设有两个图G=(V,{E})和图G’=(V’,{E`}),若V’⊆V且E’ ⊆ E,则称图G’为G的子图。
3、邻接点
对于无向图 G=(V,{E}),如果边(v,v’)∈E,则称顶点v,v’互为邻接点,即v,v’ 相邻接。边(v,v’)依附于顶点v和v’,或者说边(v,v’)与顶点v和v’ 相关联。
对于有向图G=(V,{A})而言,若弧<v,v’>∈A,则称顶点v邻接到顶点v’,顶点v’ 邻接自顶点v,或者说弧<v,v’>与顶点v,v’相关联。
4、度、入度和出度
对于无向图而言,顶点v的度是指和v相关联边的数目,记作TD(v)。
对于有向图而言,顶点v的度有入度与出度两部分,其中以顶点v为弧头的弧的数目称为该顶点的入度,记作ID(v),以顶点v为弧尾的弧的数目称为该顶点的出度,记作OD(v),顶点v的度为TD(v)=ID(v)+OD(v)。
一般的,若图G中有n个顶点,e条边或弧,则图中顶点的度与边的关系如下:
e
=
(
∑
i
=
1
n
T
D
(
v
i
)
)
/
2
e=(\displaystyle\sum_{i=1}^{n} TD(v_i))/2
e=(i=1∑nTD(vi))/2.
5、权与网
在实际应用中,图的边或弧上往往与具有一定意义的数有关,即每一条边都有与它相关的数,称为权,将带权的图叫做赋权图或网。
6、路径与回路
路径:
无向图G =(V,{E})中从顶点v到v’的路径是一个顶点序列vi0,vi1,vi2,…,vin,其中(vij-1,vij)∈E,1≤j≤n。
如果图G是有向图,则路径也是有向的,顶点序列应满足<vij-1,vij>∈A,1≤j≤n。
路径长度:
路径上经过的弧或边的数目。
回路或环:
一个路径中,若第一个顶点和最后一个顶点相同,即v =v’。
简单路径:
路径的顶点序列中的顶点各不相同。
简单回路:
除了第一个和最后一个顶点外,其余各顶点均不重复出现的回路。
7、连通图
连通图:
无向图G =(V, {E})中,若从vi到vj有路径相通,则称顶点vi与vj是连通的。如果图中任意两个顶点vi、vj∈V,vi,vj都是连通的,则称该无向图G为连通图。
连通分量:
无向图中的极大连通子图。
强连通图:
在有向图G=(V,{A})中,对于每对顶点vi、vj∈V且vi ≠ vj,从vi到vj和vj到vi都有路径。
强连通分量:
有向图的极大强连通子图称作有向图的强连通分量。
生成树:
一个连通图的生成树是指一个极小连通子图,它含有图中的全部顶点,但只有足已构成一棵树的n-1条边。两顶点间有且仅有一条路径。
图的存储结构
1、邻接矩阵表示法
也称作数组表示法。采用两个数组来表示图:
一维数组:存储顶点信息;
二维数组(邻接矩阵):存储图中顶点之间关联关系。
无权图邻接矩阵:
A
[
i
,
j
]
=
{
1
,
若<Vi,Vj>或(Vi,Vj)∈VR
0
,
反之
A[i,j] = \begin{cases} 1, & \text{若<Vi,Vj>或(Vi,Vj)∈VR} \\ 0, & \text{反之} \end{cases}
A[i,j]={1,0,若<Vi,Vj>或(Vi,Vj)∈VR反之
无向图的邻接矩阵是对称的。
网的邻接矩阵:
A [ i , j ] = { W i j , 若<Vi,Vj>或(Vi,Vj)∈VR ∞ , 反之 A[i,j] = \begin{cases} Wij, & \text{若<Vi,Vj>或(Vi,Vj)∈VR} \\ ∞, & \text{反之} \end{cases} A[i,j]={Wij,∞,若<Vi,Vj>或(Vi,Vj)∈VR反之
描述:
#define MAX_VERTEX_NUM 10 /*最多顶点个数*/
#define INFINITY 32768 /*表示极大值,即∞*/
//图的种类:DG:有向图, DN:向网, UDG表示无向图, UDN表示无向网
typedef enum{DG, DN, UDG, UDN} GraphKind;
typedef char VertexData; /*假设顶点数据为字符型*/
typedef struct ArcNode{
AdjType adj; /*无权图用1、0表示邻接;带权图则为权值类型*/
OtherInfo info;
} ArcNode;
typedef struct
{ VertexData vexs[MAX_VERTEX_NUM]; /*顶点向量*/
ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*邻接矩阵*/
int vexnum, arcnum; /*图的顶点数和弧数*/
GraphKind kind; /*图的种类标志*/
} AdjMatrix;
存储特点:
- 存储空间:
无向图:邻接矩阵对称,可采用压缩存储法(下三角),其存储空间只需n(n-1)/2;
有向图:弧有方向,邻接矩阵不一定是对称矩阵,需要 n 2 n^2 n2个存储空间。 - 便于运算:
根据 Aij = 0/1 判定图中任意两个顶点之间是否有边;
便于求得各个顶点的度。
顶点的度:
无向图:
第i个顶点的度 = 第i行元素之和,
T
D
(
v
i
)
=
∑
j
=
1
n
A
i
j
TD(v_i)=\displaystyle\sum_{j=1}^{n}A_{ij}
TD(vi)=j=1∑nAij.
有向图:
第i个顶点的出度 = 第i行元素之和,
O
D
(
v
i
)
=
∑
j
=
1
n
A
i
j
OD(v_i)=\displaystyle\sum_{j=1}^{n}A_{ij}
OD(vi)=j=1∑nAij.
第i个顶点的入度 = 第i列元素之和,
I
D
(
v
i
)
=
∑
j
=
1
n
A
i
j
ID(v_i)=\displaystyle\sum_{j=1}^{n}A_{ij}
ID(vi)=j=1∑nAij.
基本操作:
采用邻接矩阵存储法,很便于实现图的一些基本操作,如 FirstAdjVertex(G,v):
1)由LocateVertex(G,v)
找到 v 在图中的位置,即 v 在一维数组 vexs 中的序号i。
2)二维数组 arcs 中第i行上第一个 adj 域非零的分量所在的列号j,便是 v 的第一个邻接点在图 G 中的位置。
3)取出一维数组vexs[j]
中的数据信息。
注意:稀疏图不适于用邻接矩阵来存储,会造成存储空间的浪费。
创建有向图:
int LocateVertex(AdjMatrix * G, VertexData v) /*求顶点位置函数*/
{
int j=Error,k;
for(k=0;k<G->vexnum;k++)
if(G->vertex[k]==v)
{ j=k; break; }
return j;
}
int CreateDN(AdjMatrix *G)
{ /*创建一个有向网*/
int i, j, k, weight; VertexData v1, v2;
scanf("%d,%d",&G->arcnum, &G->vexnum); /*输入顶点数和弧数*/
for(i = 0; i < G->vexnum; i++)
for(j = 0; j < G->vexnum; j++)
G->arcs[i][j].adj=INFINITY; /*邻接矩阵初始化*/
for(i = 0; i < G->vexnum; i++)
scanf("%c", &G->vexs[i]); /* 输入图的顶点*/
for(k = 0; k < G->arcnum; k++)
{
scanf("%c,%c,%d", &v1, &v2, &weight); /*输入一条弧的两个顶点及权值*/
i = LocateVex_M(G, v1);
j = LocateVex_M(G, v2);
G->arcs[i][j].adj = weight; /*建立弧*/
}
return(Ok);
}
时间复杂度为O(
n
2
n^2
n2+e×n);
O(
n
2
n^2
n2)为二维数组arcs初始化。O(e×n)为边权赋值。
2、邻接表表示法
稀疏图用邻接矩阵表示法会造成空间浪费。
邻接表表示法是图的一种链式存储结构,基本思想是只存有关联的信息,一个n个顶点的图的邻接表表示由表头结点表与边表两部分构成。
表头结点表:
所有表头结点以顺序结构(向量)的形式存储,以便可以随机访问任一顶点的单链表。
数据域(vexdata):存储顶点的名或其它有关信息;
链域(firstarc):指向链表中第一个顶点(即与顶点vi邻接的第一个邻接点)。
边表:
由表示图中顶点间邻接关系的n个边链表组成。
邻接点域(adjvex):存放与顶点vi相邻接的顶点在图中的位置;
链域(nextarc):指向与顶点vi相关联的下一条边或弧的结点;
数据域(info):存放与边或弧相关的信息(如赋权图中每条边或弧的权值等)。
类似树的孩子表示法。
定义:
#define MAX_VERTEX_NUM 10 /*最多顶点个数*/
typedef enum{DG, DN, UDG, UDN} GraphKind; /*图种类*/
typedef struct ArcNode{
int adjvex; /*该弧指向顶点的位置*/
struct ArcNode *nextarc; /*指向下一条弧的指针*/
OtherInfo info; /*与该弧相关的信息*/
} ArcNode;
typedef struct VertexNode{
VertexData data; /*顶点数据*/
ArcNode *firstarc; /*指向该顶点第一条弧的指针*/
} VertexNode;
typedef struct{
VertexNode vertex[MAX_VERTEX_NUM];
int vexnum, arcnum; /*图的顶点数和弧数*/
GraphKind kind; /*图的种类标志*/
}AdjList; /*基于邻接表的图(Adjacency List Graph)*/
存储空间:
n个顶点,e条边的无向图,需要n个表头结点和2e个表结点。
无向图的度:
顶点vi的度恰好就是第i个边链表上结点的个数。
有向图的度:
第i个边链表上顶点的个数是顶点的出度。
要求顶点vi的入度,则必须遍历整个邻接表。在所有单链表中查找邻接点域的值为i的结点并计数求和。
逆邻接表法:(解决求入度不方便的问题)
可对图中的每一顶点vi建立一个逆邻接表;
对每个顶点vi建立一个所有以顶点vi为弧头的弧的表;
顶点vi的入度是逆邻接表中第i个顶点的边链表中结点个数。
3、十字链表
十字链表(Orthogonal List)是有向图的另一种链式存储结构,可以看成将有向图的邻接表和逆邻接表结合起来形成的一种链表。
① 每条弧对应一个弧结点;
② 每个顶点对应一个顶点结点,顺序存储。
有向图G1的十字链表:
建立有向图的十字链表算法:
void CrtOrthList(OrthList g)
{/*输入n个顶点和e条弧信息,建立有向图十字链表*/
scanf(“%d,%d”, &n, &e);/*键盘输入图的顶点个数和弧的个数*/
for(i = 0; i < n; i++)
{//结点顺序表初始化
scanf(“%c”, g.vertex[i].data);
g.vertex[i].firstin = g.vertex[i].firsout = NULL;
}
for (k = 0; k < e; k++)
{//建弧链表
scanf(“%c,%c”, &vt, &vh);
i = LocateVertex(g,vt);
j = LocateVertex(g,vh);
p = (ArcNode *)malloc(sizeof(ArcNode));
p->tailvex=i;p->headvex=j;//确定弧头尾
p->tlink = g.vertex[i].firstout;
g.vertex[i].firstout =p;//头插法
p->hlink = g.vertex[j].firstin;
g.vertex[j].firstin =p;
}
}/* CrtOrthList */
采用十字链表作为存储结构,很容易求出顶点的度。
既容易找到以顶点为尾的弧,也容易找到以顶点为头的弧。
4、邻接多重表
能够提供更为方便的边处理信息。
将图中关于一条边的信息用一个结点来表示;
提供标志域标记边是否被搜索过。
结构类型说明:
typedef struct EdgeNode {
int mark, ivex, jvex;
struct EdgeNode *ilink, *jlink;
}EdgeNode;
typedef struct {
VertexData data;
EdgeNode *firstedge;
}VertexNode;
typedef struct{
VertexNode vertex[MAX_VERTEX_NUM];
int vexnum, arcnum; /*图的顶点数和弧数*/
GraphKind kind; /*图的种类*/
} AdjMultiList;