一,图的定义

什么是图呢?
图是一种复杂的非线性结构。
在线性结构中,数据元素之间满足唯一的线性关系,每个数据元素(除第一个和最后一个外)只有一个直接前趋和一个直接后继;
在树形结构中,数据元素之间有着明显的层次关系,并且每个数据元素只与上一层中的一个元素(双亲节点)及下一层的多个元素(孩子节点)相关;
而在图形结构中,节点之间的关系是任意的,图中任意两个数据元素之间都有可能相关。
图G由两个集合V(顶点Vertex)和E(边Edge)组成,定义为G=(V,E)

二,图相关的概念和术语

1,无向图和有向图

对于一个图,若每条边都是没有方向的,则称该图为无向图。图示如下:


因此,(Vi,Vj)和(Vj,Vi)表示的是同一条边。注意,无向图是用小括号,而下面介绍的有向图是用尖括号。
无向图的顶点集和边集分别表示为:
V(G)={V1,V2,V3,V4,V5}
E(G)={(V1,V2),(V1,V4),(V2,V3),(V2,V5),(V3,V4),(V3,V5),(V4,V5)}
对于一个图G,若每条边都是有方向的,则称该图为有向图。图示如下。


因此,<Vi,Vj>和<Vj,Vi>是两条不同的有向边。注意,有向边又称为弧。
有向图的顶点集和边集分别表示为:
V(G)={V1,V2,V3}
E(G)={<V1,V2>,<V2,V3>,<V3,V1>,<V1,V3>}

2,无向完全图和有向完全图

我们将具有n(n-1)/2条边的无向图称为无向完全图。同理,将具有n(n-1)条边的有向图称为有向完全图。

3,顶点的度

对于无向图,顶点的度表示以该顶点作为一个端点的边的数目。比如,图(a)无向图中顶点V3的度D(V3)=3
对于有向图,顶点的度分为入度和出度。入度表示以该顶点为终点的入边数目,出度是以该顶点为起点的出边数目,该顶点的度等于其入度和出度之和。比如,顶点V1的入度ID(V1)=1,出度OD(V1)=2,所以D(V1)=ID(V1)+OD(V1)=1+2=3
记住,不管是无向图还是有向图,顶点数n,边数e和顶点的度数有如下关系:


因此,就拿有向图(b)来举例,由公式可以得到图G的边数e=(D(V1)+D(V2)+D(V3))/2=(3+2+3)/2=4

4,子图

故名思义,这个就不解释了。

5,路径,路径长度和回路

路径,比如在无向图G中,存在一个顶点序列Vp,Vi1,Vi2,Vi3…,Vim,Vq,使得(Vp,Vi1),(Vi1,Vi2),…,(Vim,Vq)均属于边集E(G),则称顶点Vp到Vq存在一条路径。
路径长度,是指一条路径上经过的边的数量。
回路,指一条路径的起点和终点为同一个顶点。

6,连通图(无向图)

连通图是指图G中任意两个顶点Vi和Vj都连通,则称为连通图。比如图(b)就是连通图。下面是一个非连通图的例子。


上图中,因为V5和V6是单独的,所以是非连通图

7,强连通图(有向图)

强连通图是对于有向图而言的,与无向图的连通图类似。

8,网

带”权值”的连通图称为网。如图所示。


三,图的创建和遍历

1.图的两种存储结构

    图的存储结构相比较线性表与树来说就复杂很多。
    我们回顾下,对于线性表来说,是一对一的关系,所以用数组或者链表均可简单存放。树结构是一对多的关系,所以我们要将数组和链表的特性结合在一起才能更好的存放
    那么我们的图,是多对多的情况,另外图上的任何一个顶点都可以被看作是第一个顶点,任一顶点的邻接点之间也不存在次序关系。
    因为任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系(内存物理位置是线性的,图的元素关系是平面的)。
    如果用多重链表来描述倒是可以做到,但在几节课前的树章节我们已经讨论过,纯粹用多重链表导致的浪费是无法想像的(如果各个顶点的度数相差太大,就会造成巨大的浪费)。
    所幸,前辈们已经帮想好了出路,我们接下来会谈图的五种不同的存储结构,大家做好准备哦~
    



1) 邻接矩阵(无向图)

考虑到图是由顶点和边或弧两部分组成,合在一起比较困难,那就很自然地考虑到分为两个结构来分别存储。
顶点因为不区分大小、主次,所以用一个一维数组来存储是狠不错的选择。
而边或弧由于是顶点与顶点之间的关系,一维数组肯定就搞不定了,那我们不妨考虑用一个二维数组来存储。
于是我们的邻接矩阵方案就诞生了!
图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。

我们可以设置两个数组,顶点数组为vertex[4]={V0,V1,V2,V3},边数组arc[4][4]为对称矩阵(0表示不存在顶点间的边,1表示顶点间存在边)。
对称矩阵:所谓对称矩阵就是n阶矩阵的元满足a[i][j]=a[j][i](0<=i,j<=n)。即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的

有了这个二维数组组成的对称矩阵,我们就可以很容易地知道图中的信息:
要判定任意两顶点是否有边无边就非常容易了;
要知道某个顶点的度,其实就是这个顶点Vi在邻接矩阵中第i行(或第i列)的元素之和;
求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点咯。

2)邻接矩阵(有向图)

无向图的边构成了一个对称矩阵,貌似浪费了一半的空间,那如果是有向图来存放,会不会把资源都利用得很好呢?


可见顶点数组vertex[4]={V0,V1,V2,V3},弧数组arc[4][4]也是一个矩阵,但因为是有向图,所以这个矩阵并不对称,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1没有弧,因此arc[0][1]=0。
另外有向图是有讲究的,要考虑入度和出度,顶点V1的入度为1,正好是第V1列的各数之和,顶点V1的出度为2,正好是第V1行的各数之和。

3)邻接矩阵(网)

在图的术语中,我们提到了网这个概念,事实上也就是每条边上带有权的图就叫网



4) 邻接表(无向图)

邻接矩阵看上去是个不错的选择,首先是容易理解,第二是索引和编排都很舒服~
但是我们也发现,对于边数相对顶点较少的图,这种结构无疑是存在对存储空间的极大浪费

因此我们可以考虑另外一种存储结构方式,例如把数组与链表结合一起来存储,这种方式在图结构也适用,我们称为邻接表(AdjacencyList)。

邻接表的处理方法是这样:
图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易地读取顶点信息,更加方便。
图中每个顶点Vi的所有邻接点构成一个线性表,由于邻接点的个数不确定,所以我们选择用单链表来存储。


5)邻接表(有向图)

若是有向图,邻接表结构也是类似的,我们先来看下把顶点当弧尾建立的邻接表,这样很容易就可以得到每个顶点的出度:


但也有时为了便于确定顶点的入度或以顶点为弧头的弧,我们可以建立一个有向图的逆邻接表:


邻接表(网)

对于带权值的网图,可以在边表结点定义中再增加一个数据域来存储权值即可:



2,图的两种遍历方法
1) 深度优先搜索遍历
深度优先搜索DFS遍历类似于树的前序遍历。其基本思路是:
a) 假设初始状态是图中所有顶点都未曾访问过,则可从图G中任意一顶点v为初始出发点,首先访问出发点v,并将其标记为已访问过。
b) 然后依次从v出发搜索v的每个邻接点w,若w未曾访问过,则以w作为新的出发点出发,继续进行深度优先遍历,直到图中所有和v有路径相通的顶点都被访问到。
c) 若此时图中仍有顶点未被访问,则另选一个未曾访问的顶点作为起点,重复上述步骤,直到图中所有顶点都被访问到为止。
图示如下:


注:红色数字代表遍历的先后顺序,所以图(e)无向图的深度优先遍历的顶点访问序列为:V0,V1,V2,V5,V4,V6,V3,V7,V8
如果采用邻接矩阵存储,则时间复杂度为O(n2);当采用邻接表时时间复杂度为O(n+e)。

2) 广度优先搜索遍历
广度优先搜索遍历BFS类似于树的按层次遍历。其基本思路是:
a) 首先访问出发点Vi
b) 接着依次访问Vi的所有未被访问过的邻接点Vi1,Vi2,Vi3,…,Vit并均标记为已访问过。
c) 然后再按照Vi1,Vi2,… ,Vit的次序,访问每一个顶点的所有未曾访问过的顶点并均标记为已访问过,依此类推,直到图中所有和初始出发点Vi有路径相通的顶点都被访问过为止。


因此,图(f)采用广义优先搜索遍历以V0为出发点的顶点序列为:V0,V1,V3,V4,V2,V6,V8,V5,V7
如果采用邻接矩阵存储,则时间复杂度为O(n2),若采用邻接表,则时间复杂度为O(n+e)。

四,最小生成树和最短路径
1,最小生成树
什么是最小生成树呢?在弄清什么是最小生成树之前,我们需要弄清什么是生成树?
用一句语简单概括生成树就是:生成树是将图中所有顶点以最少的边连通的子图。
比如图(g)可以同时得到两个生成树图(h)和图(i)


知道了什么是生成树之后,我们就很容易理解什么是最小生成树了。所谓最小生成树,用一句话总结就是:权值和最小的生成树就是最小生成树。
比如上图中的两个生成树,生成树1和生成树2,生成树1的权值和为:12,生成树2的权值为:14,我们可以证明图(h)生成树1就是图(g)的最小生成树。
那么如何构造最小生成树呢?可以使用普里姆算法。

2,最短路径
求最短路径也就是求最短路径长度。下面是一个带权值的有向图,表格中分别列出了顶点V1其它各顶点的最短路径长度。


源点 最短路径 终点   路径长度
V1 V1,V3,V2 V2 中转 5
V1 V1,V3 V3 直达 3
V1 V1,V3,V2,V4 V4 中转 10
V1 V1,V3,V5 V5 中转 18

表:顶点V1到其它各顶点的最短路径表
从图中可以看出,顶点V1到V4的路径有3条(V1,V2,V4),(V1,V4),(V1,V3,V2,V4),其路径长度分别为15,20和10,因此,V1到V4的最短路径为(V1,V3,V2,V4)。
那么如何求带权有向图的最短路径长度呢?可以使用迪杰斯特拉(Dijkstra)算法。













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值