无向图(Undigraph)的介绍
引入
- 生活中的图,有地图,集成电路板的图,可以看类似的看做是数据结构中的图
- 数据有"一对一",“一对多”和“多对多”的关系,前两种分别表示线性表和树的存储结构性质,而多对多则可表示图的存储结构性质
定义
- 图是由有限的(并且可能是可变的)组的顶点(vertices,或称点points,结点nodes),以及一系列由这些每两个顶点之间相连的有向或无向的边(edges,或称链接links,线lines),组合而成的一种数据结构
图的分类
按照连接两个顶点的边的不同,可以把图分为以下两种:
- 无向图:边仅仅连接两个顶点,没有其他含义;
- 有向图:边不仅连接两个顶点,并且具有方向;
图中的术语
- 和相邻顶点: 当两个顶点通过一边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点。
- 度:某个顶点的度就是依附于该顶点的边的个数
- 子图:是一幅图中由一部分的边及其连接的顶点的组成的子集;
- 路径:由边顺序连接的一系列的顶点组成
- 环:是一条至少含有一条边且终点和起点相同的路径(示例图中的红色环)
- 连通图:如果图中任意一个顶点都存在一条路径到达另外一 个顶点,那么这幅图就称之为连通图
- 连通子图:一个非连通图由若干连通的部分组成,每一个连通的部分 都可以称为该图的连通子图
无向图
图的存储结构
要表示一幅图,只需要表示清楚以下两部分内容即可:
- 图中所有的顶点;
- 所有连接顶点的边;
常见的图的存储结构有两种:邻接矩阵和邻接表
邻接矩阵
3. 使用一个V*V的二维数组int[V][V] adj,把索引的值看做是顶点; .
4. 如果顶点v和顶点w相连,我们只需要将adj[v][w]和adj[w][v]的值设置为1,否则设置为0即可。
解释:
使用二维数组表示一个图,二维数组有两个维度的索引,这两套索引都表示图的顶点,当我们要查看两个顶点之间是否相连通时,例如查看顶点A是否连通顶点B(存在方向A→B),我们就查看这两个顶点一维索引A和二维索引B对应的值即可,这里我们定义如果值为1就表示连通,如果为0则表示不连通。
很明显,邻接矩阵这种存储方式的空间复杂度是V^2的,如果我们处理的问题规模比较大的话,内存空间极有可能不够用。
邻接表
- 使用一个大小为V的数组Queue[V] adj ,把索弓|看做是顶点; .
- 每个索|处adj[V]存储了一个队列,该队列中存储的是所有与该顶点相邻的其他顶点储存相连的顶点的数据结构是队列,有序的
解释:邻接表由一个数组储存图的顶点,这个数组连接了与顶点数量相同的边,这些边用队列queue存储,如果图中A顶点与B相连(无方向,互相连通),则B会出现在A顶点连接的queue中,同时A也会出现在B连接的queue中
使用邻接表可以很大的减少不必要的空间浪费
主要方法与属性
- self.vertex 表示传入图中定义的顶点数量
- self.edge 边的数量,初始化为0,随着添加边的方法的调用而增加
- self.adj_list 一个列表,对应的是上图中使用的邻接表,索引代表顶点,每个索引中又储存了一个包含它的所有相邻边的列表
- add_edge(x, y) 将传入的两个顶点相连,构造一条边
- get_edges_of(v) 获取顶点v的相邻边
无向图Python代码实现与测试
class Undigraph:def __init__(self, v):self.vertex = vself.edge = 0self.adj_list = [[] for _ in range(v)]def num_of_vertices(self):return self.vertexdef num_of_edges(self):return self.edgedef add_edge(self, x: int, y: int):"""Cuz this is a undirected graph, x -> v equals to v -> x"""if y not in self.adj_list[x]:self.adj_list[x].append(y)if x not in self.adj_list[y]:self.adj_list[y].append(x)self.edge += 1def get_edges_of(self, v):return self.adj_list[v]if __name__ == '__main__':UG = Undigraph(10)UG.add_edge(2, 5)UG.add_edge(1, 4)UG.add_edge(3, 9)UG.add_edge(6, 7)UG.add_edge(2, 7)UG.add_edge(2, 8)print(UG.get_edges_of(2))print(UG.num_of_edges())print(UG.num_of_vertices())
运行结果
[5, 7, 8]
6
10