图的基本术语
图通常用一个二元组G=<V, E>表示,V表示顶点集,E表示边集。|V|表示顶点集中元素的个数,即顶点数,n个顶点的图称为n阶图。|E|表示边集中元素的个数,即边数。
注意:顶点集V和边集E均为有限集合,其中E可以为空集,V不可以为空集,但在运算中,可能产生V为空集。V为空集的图称为空图,记为φ。
1.无向图
若图G中每条边都是没有方向的,则称为无向图。每条边都是两个顶点组成的无序对,例如顶点v1和顶点v3之间的边,记为(v1, v3)或(v3, v1),
2.有向图
若图G中每条边都是有方向的,则称为有向图。有向边也称为弧,每条弧都是由两个顶点组成的有序对,例如从顶点v1到顶点v3的弧,记为<v1, v3>,v1称为弧尾,v3称为弧头
注意:尖括号<vi, vj>表示有序对,圆括号(vi, vj)表示无序对。
3.简单图
既不含平行边也不含环的图称为简单图,上图均为简单图。
在无向图中,若关联一对顶点的无向边多于一条,则称这些边为平行边,平行边的条数称为重数,如图(a)所示。在有向图中,若关联一对顶点的有向边多于一条,并且这些边的始点和终点相同(方向一致),则称这些边为平行边,如图(b)所示。自环是指一条边关联的两个顶点为同一个顶点,也就是说自己到自己有一条边,如图(c)所示。含有平行边的图称为多重图。平行边的条数称为重数。
4.完全图
在无向图中,若任意两个点都有一条边,则该图称为无向完全图。含有n个顶点的无向图,每个顶点到其他的n-1个顶点都有边,一共有n(n-1)/2条边。
在有向图中,若任意两个点都有两条方向相反的两条弧,则该图称为有向完全图。含有n个顶点的有向图,每个顶点发出n-1条边,并且进来n-1条边,一共有n(n-1)条边。
5.稀疏图和稠密图
有很少边或弧的图称为稀疏图,反之,则称为稠密图。这是一个非常模糊的概念,很难讲多少算稀疏,多少算稠密,一般来说,若图G满足|E|<|V|×log|V|,则称G为稀疏图。
6.网
在实际应用中,经常在边上标注如距离、时间、耗费等数值,该数值称为边的权值。带权的图称为网
7.邻接和关联
邻接是指顶点和顶点之间的关系,关联是指边和顶点之间的关系。有边/弧相连的两个顶点之间的关系,如无向边(vi, vj),则称vi和vj互为邻接点;有向边<vi, vj>,则称vi邻接到vj, vj邻接于vi。若存在(vi,vj)或<vi,vj>,则称该边或弧关联于vi和vj。在图中,每条边关联(依附)两个顶点。
8.顶点的度
顶点的度是指与该顶点相关联的边的数目,记为TD(v)。
在计算度数之和时,每条边算了两次。如果在计算度数时,每算一度划一条线,则可以看出每条边被计算了两次。
在有向图中,顶点的度又分为入度和出度。顶点v的入度是以v为终点的有向边的条数,记作ID(v),即进来的边数。顶点v的出度是以v为始点的有向边的条数,记作OD(v),即发出的边数。顶点v的度等于其入度和出度之和,即
TD (v)=ID(v)+OD(v)
例如,在图7-12中,顶点v1的入度为1,出度为3,度为入度和出度之和4,所有顶点的入度之和为8,所有顶点的出度之和也为8,图中的边数也为8。所有顶点的入度之和=出度之和=边数。
9.路径、路径长度和距离
路径:接续的边的顶点构成的序列。
路径长度:路径上边或弧的数目。
距离:从顶点到另一顶点的最短路径长度。
例如,在图7-13中,
s、v1、v3、t是s到t的一条路径,路径长度为3;
s、v2、v4、v3、t也是s到t的一条路径,路径长度为4;
两个顶点之间的路径有可能有很多个,路径长度最短的为两个顶点的距离,如s到t的距离为3。
注意:在有向图中,路径必须沿着箭头的方向走,无向图只要有边就可以走。
10.回路(环)、简单路径和简单回路
回路(环):第一个顶点和最后一个顶点相同的路径。在图7-13中,v2、v4、v3、v2是回路。
简单路径:除路径起点和终点可以相同外,其余顶点均不相同的路径。在图7-13中,s、v2、v4、v3、t是简单路径,而s、v2、v4、v3、v2、v1不是简单路径。
简单回路:除路径起点和终点相同外,其余顶点均不相同的路径。在图7-13中,v2、v4、v3、v2是简单回路。
11.子图与生成子图
子图:设有两个图G=(V, E)、G1=(V1, E1),若V1⊆V, E1⊆ E,则称G1是G的子图。从图中选择若干个顶点、若干条边构成的图称为原图的子图。
生成子图:从图中选择所有顶点,若干条边构成的图称为原图的生成子图。如图7-14所示,(b)、(c)是(a)的子图,(b)是(a)的生成子图。“生成”两个字的含义就是包含所有顶点。
12.连通图和连通分量
连通图:在无向图中,如果顶点vi到vj有路径,则称vi和vj是连通的。如果图中任何两个顶点都是连通的,则称G为连通图。例如,图7-14(a)是连通图。
连通分量:无向图G的极大连通子图称为G的连通分量。极大连通子图意思是:该子图是G的连通子图,如果再加入一个顶点,该子图不连通。例如,图7-15中有3个连通分量,如图7-16所示。
13.强连通图和强连通分量
强连通图:在有向图中,如果图中任何两个顶点vi到vj有路径,且vj到vi也有路径,则称G为强连通图。
强连通分量:有向图G的极大强连通子图称为G的强连通分量。极大强连通子图意思是:该子图是G的强连通子图,如果再加入一个顶点,该子图不再是强连通的。如图7-17所示,(a)是强连通图,(b)不是强连通图,(c)是(b)的强连通分量。
14.树和有向树
从图论的角度来看,树是一个无环连通图。一个含n个顶点、m条边的图,只要满足下列5个条件之一就是一棵树:
- · G是连通图且m=n-1;
- · G是连通图且无环;
- · G是连通图,但删除任意一条边就不连通;
- · G是无环图,但添加任意一条边就会产生环;
- · G中任意一对顶点之间仅存在一条简单路径。
有向树:只有一个顶点入度为0,其余顶点入度均为1的有向图,如图7-18所示。
15.生成树和生成森林
极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边,该子图不再连通。例如在图7-19中,(b)是(a)的极小连通子图,(c)不是(a)的极小连通子图。
生成树:包含无向图G所有顶点的极小连通子图。如图7-19(b)所示。因为生成树包含所有顶点,因此只有连通图才有生成树,而非连通图,每一个连通分量会有一棵生成树。
生成森林:对非连通图,由各个连通分量的生成树组成的集合。例如,图7-15中的3个连通分量,每个连通分量得到一棵生成树,称为生成森林,如图7-20所示。
图的存储结构
图的结构比较复杂,任何两个顶点之间都可能有关系。如果采用顺序存储,则需要使用二维数组表示元素之间的关系,即邻接矩阵(adjacency matrix),也可以使用边集数组,把每条边顺序存储起来。如果采用链式存储,则有邻接表、十字链表和邻接多重表等表示方法。其中,邻接矩阵和邻接表是最简单、最常用的存储方法。
邻接矩阵
邻接矩阵是表示顶点之间关系的矩阵。邻接矩阵存储方法,需要用一个一维数组存储图中顶点的信息,用一个二维数组存储图中顶点之间的邻接关系,存储顶点之间邻接关系的二维数组称为邻接矩阵。
(1)无向图的邻接矩阵![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bcd3a8787d18a9870cfacdd19b39d096.png)
无向图邻接矩阵的特点如下。
- 1)无向图的邻接矩阵是对称矩阵,并且是唯一的。
- 2)第i行或第i列非零元素的个数正好是第i个顶点的度。
图7-23中的邻接矩阵,第3列非零元素个数为2,说明第3个顶点(c)的度为2。
(2)有向图的邻接矩阵
例如,图7-24所示的有向图,其顶点信息和邻接矩阵如图7-25所示。在图7-24中,a到b有边,a、b在一维数组中的存储位置分别为0、1,因此M[0][1]=1。有向图中是有向边,a到b有边,b到a不一定有边,因此有向图的邻接矩阵不一定是对称的。
有向图邻接矩阵的特点如下:
- 1)有向图的邻接矩阵不一定是对称的。
- 2)第i行非零元素的个数正好是第i个顶点的出度,第i列非零元素的个数正好是第i个顶点的入度
图7-25所示的邻接矩阵,第3行非零元素个数为2,第3列非零元素个数也为2,说明第3个顶点(c)的出度和入度均为2。
(3)网的邻接矩阵
邻接矩阵的数据结构定义
邻接矩阵的存储方法
算法步骤
1)输入顶点数和边数。
2)依次输入顶点信息,存储到顶点数组Vex[]中。
3)初始化邻接矩阵,如果是图,则初始化为0;如果是网,则初始化为∞。
4)依次输入每条边依附的两个顶点,如果是网,还需要输入该边的权值。
- · 如果是无向图,则输入两个顶点a、b,查询a、b在顶点数组Vex[]中的存储下标i、j,令Edge[i][j]=Edge[j][i]=1。
- · 如果是有向图,则输入两个顶点a、b,查询a、b在顶点数组Ve x[]中的存储下标i、j,令Edge[i][j]=1。
- · 如果是无向网,则输入两个顶点及权值a、b、w,查询a、b在顶点数组Vex[]中的存储下标i、j,令Edge[i][j]=Edge[j][i]=w。
- · 如果是有向网,则输入两个顶点及权值a、b、w,查询a、b在顶点数组Vex[]中的存储下标i、j,令Edge[i][j]=w。
算法图解
邻接表优缺点
优点:
缺点:
邻接表
邻接表(Adjacency List)是图的一种链式存储方法。邻接表包含两部分:顶点和邻接点。顶点包括顶点信息和指向第一个邻接点的指针。邻接点包括邻接点的存储下标和指向下一个邻接点的指针。顶点vi的所有邻接点构成一个单链表。
(1)无向图的邻接表
例如,一个无向图如图7-37所示,其邻接表如图7-38所示。
a的邻接点是b、d,其邻接点的存储下标为1、3,按照头插法(逆序)将其放入a后面的单链表中。
b的邻接点是a、c、d,其邻接点的存储下标为0、2、3,将其放入b后面的单链表中。
c的邻接点是b、d,其邻接点的存储下标为1、3,将其放入c后面的单链表中。
d的邻接点是a、b、c,其邻接点的存储下标为0、1、2,将其放入d后面的单链表中。
无向图邻接表的特点如下。
- 1)如果无向图有n个顶点、e条边,则顶点表有n个节点,邻接点表有2e个节点。
- 2)顶点的度为该顶点后面单链表中的节点数。
(2)有向图的邻接表(出边)
例如,一个有向图如图7-39所示,其邻接表如图7-40所示。
a的邻接点(只看出边,即出弧)是b、c、e,其邻接点的存储下标为1、2、4,按照头插法(逆序)将其放入a后面的单链表中。
b的邻接点是c,其邻接点的存储下标为2,将其放入b后面的单链表中。
c的邻接点是d、e,其邻接点的存储下标为3、4,按头插法将其放入c后面的单链表中。
d的邻接点是e,其邻接点的存储下标为4,将其放入d后面的单链表中。
e的没有邻接点,其后面单链表为空。
注意:有向图顶点的邻接点,只看该顶点的出边(出弧)。
有向图邻接表的特点如下
- 1)如果有向图有n个顶点、e条边,则顶点表有n个节点,邻接点表有e个节点。
- 2)顶点的出度为该顶点后面单链表中的节点数。
在有向图邻接表中,很容易找到顶点的出度,但是找到入度就很难了,需要遍历所有邻接点表中的节点,查找该顶点出现了多少次,入度就是多少。
如图7-41所示,顶点c的下标为2,邻接表中有两个为2的节点,因此c的入度为2;顶点e的下标为4,邻接表中有两个为3个为4的节点,因此e的入度为3。
(3)有向图的逆邻接表(入边)
有时为了方便得到顶点的入度,可以建立一个有向图的逆邻接表,图7-42的逆邻接表如图7-43所示。
a没有逆邻接点(只看入边,即入弧),其后面单链表为空。
b的逆邻接点是a,其存储下标为0,将其放入b后面的单链表中。
c的逆邻接点是a、b,其存储下标为0、1,按照头插法将其放入c后面的单链表中。
d的逆邻接点是c,其存储下标为2,将其放入d后面的单链表中。
e的逆邻接点是a、c、d,其存储下标为0、2、3,按照头插法(逆序)将其放入e后面的单链表中。
注意:有向图顶点的逆邻接点,只看该顶点的入边(入弧)。
有向图逆邻接表的特点如下
- 1)如果有向图有n个顶点、e条边,则顶点表有n个节点,邻接点表有e个节点。
- 2)顶点的入度为该顶点后面单链表中的节点数。
邻接表的数据结构定义
邻接表用到2个数据结构
- 1)顶点节点,包括顶点信息和指向第一个邻接点的指针,可用一维数组存储。
- 2)邻接点节点,包括邻接点的存储下标和指向下一个邻接点的指针。顶点vi的所有邻接点构成一个单链表。
邻接点节点包含邻接点下标和指向下一个邻接点的指针,如图7-44所示。如果是网的邻接点,还需要增加一个权值域w,如图7-45所示。
邻接表的优缺点
十字链表
十字链表(Orthogonal List)是有向图的另一种链式存储结构。它结合了邻接表和逆邻接表的特性,可以快速访问出弧和入弧,得到出度和入度。十字链表也包含两部分:顶点节点和弧节点。顶点节点包括顶点信息和两个指针(分别指向第一个入弧和第一个出弧),弧节点包括两个数据域(弧尾、弧头)和两个指针域(分别指向同弧头和同弧尾的弧)。
例如,一个有向图如图7-64所示,其邻接表如图7-65所示
a的出弧是ab、ac、ae,弧尾弧头对应的存储下标为01、02、04,逆序将出弧放入a后面的单链表中。
b的出弧是bc,弧尾弧头对应的存储下标为12,将出弧放入b后面的单链表中。
c的出弧是cd、ce,弧尾弧头对应的存储下标为23、24,逆序将出弧放入c后面的单链表中。
d的出弧是de,弧尾弧头对应的存储下标为34,将出弧放入d后面的单链表中。
e没有出弧,出弧域置空。
邻接多重表
邻接多重表(adjacency multilist)是无向图的另一种链式存储结构。邻接表的关注点是顶点,而邻接多重表的关注点是边,适合对边做访问标记、删除边等操作。邻接多重表类似十字链表,也包含两部分:顶点节点和边节点。顶点节点包括顶点信息和一个指针(指向第一个依附于该顶点的边),边节点包括两个数据域(顶点i、顶点j)和两个指针域(分别指向依附于i、j的下一条边)。如果需要标记是否被访问过,边节点还可以增加一个标志域。