一、图的介绍
图是一种比较复杂的数据结构,在线性表中数据元素之间仅有线性关系,每个元素只有一个直接前驱和直接后继(元素之间只存在一对一关系),在树形结构中元素之间有着明显的层次关系,每一层的元素只能和下层的多个元素有关系(元素之间存在一对多关系),而在图形结构中,任意两个结点之间都可能有关系(元素之间存在多对多关系)。
二、图的定义和术语
顶点:图中的数据元素被称为顶点,一般使用V表示图的顶点的有穷非空集合。
弧:两个顶点之间的关系记作<v,w>,表示能从顶点v到达顶点w,也就是v能到w,但w不一定能到v,我们称v为弧尾或初始点,称w为弧头或终端点。
有向图:由弧+顶点构成的图叫有向图,也就是顶点之间是单行道。
边:两个顶点之间的关系记录(v,w),表示既能从v到w,也能从w到v,我们称v和w之间的关系是一条边。
无向图:由边+顶构成的图有无向图,顶点之间的双行道。
注意:一般使用G代表图,V顶点的集合,VR代表弧或边的集合,n代表顶点的数目,e代表边或弧的数目,我们不讨论顶点到自己的边或弧。
完全图(无向完全图):在无向图中,e的取值范围是0 ~ n*(n-1)/2,如果无向图的边的数量达到最大值这种无向图称称为完全图,即任意两个顶点之间存在边。
有向完全图:在有向图中,e的取值范围是0 ~ n(n-1),如果有向图的弧的数量达到最大值这种有向图称称为有向完全图,即任意两个顶点之间存在方向相反的弧。
稀疏图和稠密图:如果图中的边和弧很少,e<nlogn 这种图被称为稀疏图,反之称为稠密图。
权和网:如果图中的顶点到另一个顶点需要代价(距离或耗费),那么在表示边或弧的时候需要附加数据,附加的数据就叫做权,带权的图通常被称为网,这也是互联网的由来。
子图:假定有两个图G1和G2,如果G1的顶点集合是G2的顶点集合的子集,且G1的边或弧集合是G2的边或弧集合的子集,则称G1是G2的子图。
邻接点:在无向图中如果有一条边(v,w),则v,w两个顶点互为邻接点,即v和w相邻接,边(v,w)依附于顶点v,w,或者说(v,w)和顶点v、w相关联。
顶点v的度:在无向图中与顶点v相关联的边的数量。
顶点v的入度和出度:在有向图中,以顶点v作为弧头的弧的数量称为顶点的入度,以顶点v作为弧尾的弧的数量称为顶点的出度。
路径:从顶点v到达顶点w所经历的顶点序叫做路径,路径的长度就是边或弧的数目。
回路或环:起始点和终点相同的路径称为回路或环。
简单路径:路径中顶点不重复出现的路径称为简单路径。
简单回路或简单环:起始点和终点相同且其余顶点不重复出现,被称为简单回路或简单环。
连通图:在无向图中,从顶点v到顶点w有路径,则称v和w是连通的,如果图中任意两个顶点都是连通的,则称图为连通图。如果一个图中有n个顶点那么至少需要n-1条边才能达到连通图。
生成树:仅需要n-1边的连通图叫生成树,如果再配合上权重,代价最小的叫最小生成树
连通分量:G1和G2都是连通图,且G1是G2的子图,则称G1是G2的连通分量或极大连通子图。
强连通图:在有向图中,如果任意一对顶点都双向连通,则称图为强连通图。
强连通分量:G1和G2都是强连通图,且G1是G2的子图,则称G1是G2的强连通分量或极大强连通子图。
三、图的存储结构:
邻接矩阵:
-
使用一维数组存储n个顶点
-
使用二维数组存储顶点之间的边或者弧
char V[7] = {A,B,C,D,E,F,G}; char e[7][7] A B C D E F G A 0 1 1 1 0 0 0 B 1 0 0 1 0 0 0 C 1 0 0 0 0 1 0 D 1 1 0 0 1 0 1 E 0 0 0 1 0 1 0 F 0 0 1 0 1 0 1 G 0 0 0 1 0 1 0 对于二维数组e[i][j]的值为1,表示顶点V[i]与顶点V[j]之间存在边 因为不考虑顶点自己到自己的边,因此左对角线必定为0 如果存储的是无向图,数值会沿对角线对称,可以进行压缩矩阵
邻接矩阵优点:
-
计算出度、入度方便,操作简单,只需要操作顺序表
邻接矩阵缺点:
-
当图是稀疏图时,会非常浪费存储内存
邻接表:
边结构:
顶点下标
下一条边地址
顶点结构:
顶点数据
指向该顶点第一条边的指针
图结构:
由顶点结构元素组成的数组
顶点数量
优点:
存储边是通过单链表存储,所以不会浪费内存,并且计算出度方便
缺点:
计算入度比较麻烦,需要遍历所有顶点的边链表
十字链表:
-
用于存储有向图的链式结构+顺序结构
弧:
弧尾下标
弧头下标
指向弧尾相同的下一条弧
指向弧头相同的下一条弧
顶点
数据
指向第一条出度的弧
指向第一条入度的弧
图:
顶点顺序表
顶点数量
优点:计算出度、入度比较方便,并也节约内存
邻接多重表:
-
是一种存储无向图的一种顺序+链式结构
边:
与边阾接的两个顶点的下标 i j
i_next 指向下一条与i阾接的边
j_next 指向下一条与j阾接的边
四、图的遍历
深度优先(DFS)
-
对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。
-
不全部保留结点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢
广度优先(BFS)
-
又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止
-
需要借助队列实现,需要额外内存协助
-
保留全部结点,占用空间大; 无回溯操作(即无入栈、出栈操作),运行速度快
-
一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题