假期连载之十三

(图—图的基本概念、ADT、存储结构表示)

 

    graph部分涉及到了许多离散数学方面的理论知识,因此本节需要读者具备一定的离散数学背景—当然,重要的概念和性质我们会附带说明。图是一种更为灵活复杂的非线性结构,理论上讲它比树的实用性更强,但算法设计的复杂度上也存在很大提升。图最典型的应用领域有电路分析、寻找最短路线、项目规划、鉴别化合物、统计力学、遗传学、控制论、语言学以及一些社会科学。

 

    图的基本概念

 

    图是一种网状数据结构,其形式定义如下:

    Graph=(V,R);

    V={x|x属于DataObject};

    R={VR};

    VR={<x,y>|P(x,y)(x,y属于V)};

    其中DataObject是一个数据集合,其中所有的元素都具有相同的特性。V中的数据元素通常称为顶点VertexVR是两个顶点之间的关系的集合,P(x,y)表示xy之间有特定的关联属性P

    若有VR内的元素<x,y>,则其表示从顶点x到顶点y的一条Arc,并称x弧尾Tail或起始点,称y弧头Head或终端点,此时图中的边是有方向的,称这样的图为有向图

若有VR内的元素<x,y>,必有<y,x>,即VR是对称关系,这时用无序对的表示方法来代替两个有序对(x,y),表示xy之间的一条边,此时的图称为无向图。

 

n表示图中的顶点个数,用e表示图中边或弧的数目,且不考虑图中每个顶点到其自身的边或弧。对于无向图而言,如果e=n(n-1)/2,则称其为无向完全图,对应的有向图被称为有向完全图。对于e<nlogn的图称为稀疏图,否则称稠密图

子图是指一个图的顶点结构和关联不发生改变,只存在其中一部分的图。在无向图G=(V,{E})中,如果边(v1,v2)属于E,则称v1v2互为邻接点,并且边(v1,v2)依附于顶点v1v2,或者说边与v1v2相关联。而对于有向图而言,如果存在弧<v1,v2>属于A,则称顶点v1邻接到顶点v2,顶点v2邻接自顶点v1,或者说弧<v1,v2>与顶点v1v2相关联。

是指与顶点v关联的边数,记作deg(v)。在有向图中顶点为弧头则称其为入度ID(v),为弧尾则称其为出度OD(v),顶点的入度的数目必然和出度相等。

在实际应用中,往往赋给边或弧一定的数值,成为。这种带权信息的图被称为赋权图

路径是指任意两个顶点之间沿边或弧方向的顶点序列。如果G是有向图,则其路径也是有向的。在一个路径中,如果第一个顶点和最后一个顶点相同,则称该路径为一个回路。若表示路径的顶点序列中的顶点各不相同,则称这样的路径为简单路径。除第一个和最后一个顶点外,其余个顶点均不重复出现的回路称为简单回路

在无向图G中,如果从vivj有路径相通,则称其是连通的。如果对于任意两个顶点均是连通的,则图被称为连通图。无向图的极大连通子图被称为无向图的连通分量。而对有向图而言,如果对于每对顶点,都有互相连通的路径,则称其为强连通图,它的极大连通子图被称为强连通分量

现在给出图的抽象数据类型定义:

ADT Graph

{

    数据对象V:一个集合,集合中所有元素有相同的特性

    数据关系R:R={VR};

    基本操作:

    CreateGraph(G);         //创建图G

    DestoryGraph(G);        //销毁图G

    LocateVertex(G,v);      //若存在顶点v,则返回v的位置,否则返回NULL

    GetVertex(G,i);         //返回G中顶点v的第i个顶点的值

    FirstAdjVertex(G,v);    //返回G中顶点v的第i个邻接点的值

    NextAdjVertex(G,v,w);   //返回返回G中顶点v的下一个(w个后面)邻接点的值,如果w为最后一个邻接点,则返回NULL

    InsertVertex(G,u);      //添加一个点u

    DeleteVertex(G,v);      //删除图中的顶点v以及相关联的弧

    InsertArc(G,v,w);       //向图中添加一条从v指向w的弧

    DeleteArc(G,v,w);       //删除图中从v指向w的弧

    TraverseGraph(G);       //遍历图G

}

 

图的基本存储结构

 

现在,我们按照图的性质,介绍四种常用的存储表示法。大体上来说依然为顺序存储和链式存储两种类型。

1、 图的邻接矩阵表示

邻接矩阵adjacency matrix表示法实际上就是数组表示法。它采用两个数组来表示图:一个是用于存储顶点信息的一维数组,另一个是用于存储图中顶点之间关联关系的二维数组,这个关联关系数组被称为邻接矩阵

在储存关联关系的二维数组中,1代表了无权图中两顶点间路径连通,0则反之,其阶为顶点个数。

邻接矩阵表示的特点如下:

a)存储空间方面,对无向图而言,其邻接矩阵为对称矩阵,因而可采用相关的压缩存储法。但对于有向图而言,由于顶点间不一定互相连通,因此需要n^2个存储空间。

b)便于计算,由于采用了数组表示,关于图中顶点的基本运算就相对简便。综合而言,邻接矩阵不适合存储稀疏图。

    2、邻接表表示法

    邻接表adjacency matrix吸取了邻接矩阵的优点,同时凭借链式结构的特点对上述方法做出改进,在节省存储空间上起到了很大作用。邻接表对图中每个顶点建立一个带头结点的边链表。

表头结点有两部分构成:数据域vexdata以及链域firstarc,用于指向链表中第一个顶点。对于边表,由表示图中顶点间邻接关系的n个边链表构成。因而结点在包括了上述两个结点域之外,还附带一个邻接点域adjvex,用于存放与该点相邻的顶点在图中的位置。

邻接表虽然进一步节省了存储空间,但在一些基本运算过程中,例如求某个顶点的入度,则必须遍历整个邻接表,在时间复杂度上并不如邻接矩阵优异。

为了解决一些计算上的问题,还可以另外设置一个逆邻接表,就可以满足一些常用运算的需要。

3、正交链表(十字链表)

正交链表Orthogonal Lists有向图的另一种存储结构,它在邻接表表示法所不擅长的有向图运算领域做到了很好的效果。其实质是将邻接表和逆邻接表结合起来形成的一种链表。

正交链表的弧结点结构包括了tailvex弧尾顶点位置、headvex弧头顶点位置、hlink指向与此弧的弧头相同的下一条弧、tlink指向与此弧的弧尾相同的下一条弧、info该弧的相关信息。

其顶点结点包括了data数据域、firstin指向该顶点作为弧头的第一个弧结点、firstout指向该顶点作为弧尾的第一个弧结点。

4、 邻接多重表

邻接多重表Adjacency Multi_list无向图的另外一种存储结构。这种结构相较于邻接表来说结构复杂,运算算法难度较高,但在处理无向图的边信息时却大大节省了算法时间,是一种高效的无向图存储表示。

其边结点的结构包括了mark标记该条边是否已被搜索、ivex/jvex为该边依附的两个顶点在图中的位置、ilink指向下一条依附于顶点ivex的边、jlink指向下一条依附于顶点jvex的边、info边信息。

顶点结点结构包括了data数据域、firstedge指向第一条依附于该顶点的边。

以上四种存储策略分别适用于不同的需求环境,具体应采取何种存储结构应按照实际情况而定。为统一起见今后我们涉及到图的基本运算时,一般以邻接矩阵和邻接表这两个基本结构为例。

 

(未完待续)