文章目录

  • 一、图
    • 1.1、图的定义
    • 1.2、图的基本概念和术语
    • 1.3、图的存储结构
      • 1.3.1、邻接矩阵
      • 1.3.2、邻接表
      • 1.3.3、十字链表
      • 1.3.4、邻接多重表
      • 1.3.5、边集数组
    • 1.4、图的遍历

一、图

图_第1张图片
在线性表中,数据元素之间是被串起来的,仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继。
在树形结构中,数据元素之间有着明显的层次关系,并且每一层上的数据元素可能和下一层中多个元素相关,但只能和上一层中一个元素相关。
图是一种较线性表和树更加复杂的数据结构。在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。

1.1、图的定义

(Graph)是由顶点的有穷非空集合V ( G )和顶点之间边的集合E ( G )组成,通常表示为: G = ( V , E )

  • G 表示个图;
  • V 是图G中顶点的集合;
  • E是图G中边的集合;
  • V = { v 1 , v 2 , . . . , v n } ,则用∣ V ∣ 表示图G中顶点的个数,也称图G 的阶;
  • E = { ( u , v ) ∣ u ∈ V , v ∈ V } ,用∣ E ∣表示图G中边的条数。

注意: 线性表可以是空表,树可以是空树,但图不可以是空图。就是说,图中不能一个顶点也没有,图的顶点集V一定非空,但边集E可以为空,此时图中只有顶点而没有边。


1.2、图的基本概念和术语

1、有向图

  • 若E是有向边(也称弧)的有限集合时,则图G为有向图
  • 弧是顶点的有序对,记为,其中v,w是顶点;
  • v称为弧尾,w称为弧头,称为从顶点v到顶点w的弧,也称v邻接到w,或w邻接自v;

图_第2张图片
有向图G1可以如下表示:

G1 = (v1, E1)
V1 = {1, 2, 3}
E1 = {<1, 2>, <2, 1>, <2, 3>}

2、无向图

  • 若E是无向边(简称边)的有限集合时,则图G为无向图;
  • 边是顶点的无序对,记为(v, w)或(w,v),因为(v,w)=(w,v), 其中v,w是顶点;
  • 可以说顶点w和顶点v互为邻接点;
  • 边(v, w)依附于顶点w和v,或者说边(v, w)和顶点v, w相关联;

图_第3张图片
无向图G2可以如下表示:

G2 = (v2, E2)
V2 = {1, 2, 3, 4}
E2 = {<1, 2>, <1, 3>, <1, 4>, <2, 3>, <2, 4>, ❤️, 4>}

3、简单图
一个图G若满足:

  • 不存在重复边;
  • 不存在顶点到自身的边;

则称图G GG为简单图,上图中G1 和G~2`均为简单图。数据结构中仅讨论简单图。

4、多重图
若图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联,则G为多重图。多重图的定义和简单图是相对的。

5、完全图(也称为简单完全图)

  • 对于无向图,|E|的取值范围为0 ~ n(n-1)/2,有n(n-1)/2条边的无向图称为完全图,在完全图中任意两个顶点之间都存在边;
  • 对于有向图,|E|的取值范围为0 ~ n(n-1),有n(n-1)条弧的有向图称为有向完全图,在有向完全图中任意两个顶点之间都存在方向相反的两条弧;

上面的G2是无向完全图,下面的G3是有向完全图;
图_第4张图片

6、子图
设有两个图G = ( V , E )G ′ = ( V ′ , E ′ ) , 若V ′V的子集,且E ′ E的子集,则称G ′ G的子图。若有满足V ( G ′ ) = V ( G ) 的子图G ′ ,则称其为G生成子图。上图中G 3 为G 1 的子图。

注意:并非V和E的任何子集都能构成G的子图,因为这样的子集可能不是图,即E的子集中的某些边关联的顶点可能不在这个V的子集中。

7、连通、连通图和连通分量

  • 在无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的;
  • 若图G中任意两个顶点都是连通的,则称图G为连通图,否则称为非连通图
  • 无向图中的极大连通子图称为连通分量
  • 若一个图有n个顶点,并且边数小于n − 1 ,则此图必是非连通图;

如下图(a)所示, 图G4有3个连通分量,如图(b)所示:
图_第5张图片

8、强连通图、强连通分量

  • 在有向图中,若从顶点v到顶点w和从顶点w到项点v之间都有路径,则称这两个顶点是强连通的;
  • 若图中任何一对顶点都是强连通的,则称此图为强连通图
  • 有向图中的极大强连通子图称为有向图的强连通分量

注意:强连通图、强连通分量只是针对有向图而言的。一般在无向图中讨论连通性,在有向图中考虑强连通性。

9、生成树、生成森林

  • 连通图的生成树是包含图中全部顶点的一个极小连通子图;
  • 若图中顶点数为n,则它的生成树含有n − 1条边;
  • 对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路;
  • 在非连通图中,连通分量的生成树构成了非连通图的生成森林;

图G2的一个生成树,如下图所示:
图_第6张图片

10、顶点的度、入度和出度

  • 图中每个顶点的度定义:以该顶点为一个端点的边的数目
  • 对于无向图,无向图的全部顶点的度之和等于边数的2倍,因为每条边和两个顶点相关联;
  • 对于有向图,顶点v的度分为入度和出度,入度是以顶点v为终点的有向边的数目,而出度是以顶点v为起点的有向边的数目,顶点v的度等于其入度和出度之和;
  • 有向图的全部顶点的入度之和与出度之和相等,并且等于边数,这是因为每条有向边都有一个起点和终点;

11、边的权和网
在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值。这种边上带有权值的图称为带权图,也称网。

12、稠密图、稀疏图
边数很少的图称为稀疏图,反之称为稠密图。稀疏和稠密本身是模糊的概念,稀疏图和稠密图常常是相对而言的。一般当图G满足∣E∣ < ∣V∣log∣V∣时,可以将G视为稀疏图。

13、路径、路径长度和回路

  • 顶点Vp到Vq之间的一条路径,是指顶点序列;
  • 路径上边的数目称为路径长度;
  • 第一个顶点和最后一个顶点相同的路径称为回路或环;
  • 若有一个图有n个顶点,并且有大于n-1条边,则此图一定有环;

14、简单路径、简单回路

  • 在路径序列中,顶点不重复出现的路径称为简单路径;
  • 除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路;

15、距离

  • 从顶点u出发到顶点v的最短路径若存在,则此路径的长度称为从u到v的距离;
  • 若从u到v根本不存在路径,则记该距离为无穷( ∞ ) ;

16、有向树

  • 一个顶点的入度为0、其余顶点的入度均为1的有向图,称为有向树

1.3、图的存储结构

由于图的结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系,也就是说,图不可能用简单的顺序存储结构来表示。而多重链表的方式,要么会造成很多存储单元的浪费,要么又带来操作的不便。因此,对于图来说,如何对它实现物理存储是个难题,接下来我们介绍五种不同的存储结构:

1.3.1、邻接矩阵

图的邻接矩阵存储方式是用两个数组来表示图:一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。

设G有n个顶点,则邻接矩阵A是一个n*n的方阵,定义为:
图_第7张图片


下图是一个无向图和它的邻接矩阵:
图_第8张图片

  • 无向图的邻接矩阵一定是一个对称矩阵(即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的),因此,在实际存储邻接矩阵时只需存储上(或下)三角矩阵的元素;
  • 对于无向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好是第i个顶点的度,例如V1的度就是1+0+1+0=2;
  • 求顶点Vi的邻接点就是将矩阵中第i行元素扫描一遍,A[i][j]为1就是邻接点;

下图是有向图和它的邻接矩阵:
图_第9张图片

  • 主对角线上的数值依然为0,但因为是有向图,所以此矩阵并不对称;
  • 有向图讲究入度与出度,顶点v1 的入度为1,正好是第v1 列各数之和。顶点v1的出度为2,即第v1行的各数之和;
  • 与无向图同样的办法,判断顶点vi到vj是否存在弧,只需要查找矩阵中A[i][j]是否为1即可;

下图是有向网图和它的邻接矩阵:
图_第10张图片


通过以上对无向图、有向图和网的描述,可定义出邻接矩阵的存储结构:

#define MaxVertexNum 100	//顶点数目的最大值
typedef char VertexType;	//顶点的数据类型
typedef int EdgeType;	//带权图中边上权值的数据类型
typedef struct{
	VertexType Vex[MaxVertexNum];	//顶点表
	EdgeType Edge[MaxVertexNum][MaxVertexNum];	//邻接矩阵,边表
	int vexnum, arcnum;	//图的当前顶点数和弧树
}MGraph;

注意:
① 在简单应用中,可直接使用二维数组作为图的邻接矩阵(顶点信息等均可省略);
② 当邻接矩阵中的元素仅表示相应的边是否存在时,EdgeType可定义为值为0和1的枚举类型;
③ 无向图的邻接矩阵是对称矩阵,对规模特大的邻接矩阵可以采用压缩存储;
④ 邻接矩阵表示法的空间复杂度为0(n2),其中n为图的顶点数|V|;
⑤ 用邻接矩阵存储图,很容易确定图中任意两个顶点之间是否有边相连。但是,要确定图中有多少边,则必须按行、按列对每个元素进行检测,所花费的时间代价很大;
⑥ 稠密图适合使用邻接矩阵的存储表示;


1.3.2、邻接表

当一个图为稀疏图时(边数相对顶点较少),使用邻接矩阵法显然要浪费大量的存储空间,如下图所示:
图_第11张图片
而图的邻接表法结合了顺序存储和链式存储方法,大大减少了这种不必要的浪费;

邻接表:是指对图G中的每个顶点Vi建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对于有向图则是以顶点Vi为尾的弧),这个单链表就成为顶点Vi的边表(对于有向图则称为出边表)。

边表的头指针和顶点的数据信息采用顺序存储(称为顶点表),所以在邻接表中存在两种结点:顶点表结点和边表结点,如下图示:
在这里插入图片描述


无向图的邻接表的实例,如下图所示:
图_第12张图片


有向图的邻接表的实例,如下图所示:
图_第13张图片
此时我们很容易就可以算出某个顶点的入度或出度是多少,判断两顶点是否存在弧也很容易实现。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。


图的邻接表存储结构,定义如下:

#define MAXVEX 100	//图中顶点数目的最大值
type char VertexType;	//顶点类型应由用户定义
typedef int EdgeType;	//边上的权值类型应由用户定义
/*边表结点*/
typedef struct EdgeNode{
	int adjvex;	//该弧所指向的顶点的下标或者位置
	EdgeType weight;	//权值,对于非网图可以不需要
	struct EdgeNode *next;	//指向下一个邻接点
}EdgeNode;

/*顶点表结点*/
typedef struct VertexNode{
	Vertex data;	//顶点域,存储顶点信息
	EdgeNode *firstedge	//边表头指针
}VertexNode, AdjList[MAXVEX];

/*邻接表*/
typedef struct{
	AdjList adjList;
	int numVertexes, numEdges;	//图中当前顶点数和边数
}

图的邻接表存储方法具有以下特点:

  • 若G为无向图,则所需的存储空间为0(|V| + 2|E|);若G为有向图,则所需的存储空间为0(|V| + |E|);谴谪的倍数2是因为,无向图中每条边在链接表中出现了两次;
  • 对于稀疏图,采用邻接表表示将极大地节省存储空间;
  • 在邻接表中,给定一顶点,能很容易地找出它的所有邻边,因为只需要读取它的邻接表;在邻接矩阵中,相同的操作则需要扫描一行,花费的时间为O(n)。但是,若要确定给定的两个顶点间是否存在边,则在邻接矩阵中可以立刻查到,而在邻接表中则需要在相应结点对应的边表中查找另一结点,效率较低;
  • 在有向图的邻接表表示中,求一个给定顶点的出度只需计算其邻接表中的结点个数;但求其顶点的入度则需要遍历全部的邻接表。因此,也有人采用逆邻接表的存储方式来加速求解给定顶点的入度。当然,这实际上与邻接表存储方式是类似的;
  • 图的邻接表表示并不唯一,因为在每个顶点对应的单链表中,各边结点的链接次序可以是任意的,它取决于建立邻接表的算法及边的输入次序;

1.3.3、十字链表


1.3.4、邻接多重表


1.3.5、边集数组


1.4、图的遍历

你可能感兴趣的:(#,软件设计师笔记,算法,数据结构,图论)