图文结合、利于理解的数据结构学习笔记(7)——图

机械工业出版社《数据结构与算法——Python语言实现》学习笔记。
画图工具:draw.io

1 图的抽象数据结构

图是顶点和边的集合。我们将抽象模型定义为三种数据类型的集合:Vertex、Edge 和 Graph。

Vertex 是存储由使用者提供的任意元素的轻量级的对象,假设它提供一个检索所存储元素的方法 element()。

Edge 同样存储相关联的对象,假设它提供的方法如下:

  • element():检索所存储元素
  • endpoint():返回元组 (u,v),顶点 u 是边的起点,顶点 v 是终点;对于一个无向图,方向是任意的。
  • opposite():假设顶点 v 是边的一个端点(起点或者终点),返回另一个端点。

图的基本抽象表示为 Graph ADT。我们假设一个图是有向或者无向的,定义在构造时指定,Graph ADT 包含以下几种方法:

  • vertex_count():返回图的顶点的数目
  • vertices():返回图中所有顶点
  • edge_count():返回图的边的数目
  • edges():迭代返回图的所有边
  • get_edge(u,v):返回从顶点 u 到顶点 v 的边,如果其中一个存在;否则返回 None。对于无向图,get_edge(u,v) 和 get_edge(v,u) 之间没有区别。
  • degree(v, out=True):对于一个无向图,返回边入射到顶点 v 的数目。对于一个有向图,返回入射到顶点 v 的输出(或输入)边的数目,由可选参数指定。
  • incident_edges(v, out=True):返回所有边入射到顶点 v 的迭代循环。在有向图的情况下,通过默认报告输出边;如果可选参数设置为 False,则报告输出边。
  • insert_vertex(x = None):创建和返回一个新的存储元素 x 的 Vertex。
  • insert_edge(u, v, x=None):创建和返回一个新的从顶点 u 到顶点 v 的存储元素 x 的 Edge(默认 None)。
  • remove_vertex(v):移除顶点 v 和图中它的所有入射边。
  • remove_edge(e):移除图中的边 e。

2 图的数据结构

介绍四种表示图的数据类型,在四种表示中都维护一个集合来存储图的 顶点 ,然而这四种表示在它们组织 的方式上有显著不同。

  • 边列表 中,对所有边采用无序的列表,这可以满足最低限度,但无有效办法来找到特定的边 (u, v) ,或者找到入射到顶点 v 的所有边。
  • 邻接列表 中,为每个顶点维护一个单独的列表,包括入射到顶点的那些边。可以通过取并集来确定完整的边集合,优点是可以更高效地找到所有入射到给出顶点的边。
  • 邻接图 和邻接列表非常相似,每个顶点保存为一个次级图,其中邻接的点为主键,连接的边作为相关联的值。邻接图允许在 O(1) 的预期时间内访问特定边 (u,v)。
  • 邻接矩阵 对于有 n 个顶点的图维持一个 n x n 矩阵,最坏的情况下访问特定边 (u,v) 的时间为 O(1)。矩阵中每一项专用于为顶点 u 和 v 的特定对存储一个参考边 (u,v) ;没有这样的边存在则该表项为空。

4 种结构的性能总结:

操作边列表邻接列表邻接图邻接矩阵
vertex_count()O(1)O(1)O(1)O(1)
edge_count()O(1)O(1)O(1)O(1)
vertices()O(n)O(n)O(n)O(n)
edges()O(m)O(m)O(m)O(m)
get_edge(u,v)O(m)O(min( d u d_u du, d v d_v dv))O(1)exp.O(1)
degree(v)O(m)O(1)O(1)O(n)
incident_edges(v)O(m)O( d v d_v dv)O( d v d_v dv)O(n)
insert_vertex(x)O(1)O(1)O(1)O( n 2 n^2 n2)
remove_vertex(v)O(m)O( d v d_v dv)O( d v d_v dv)O( n 2 n^2 n2)
insert_edge(u, v, x)O(1)O(1)O(1)exp.O(1)
remove_edge(e)O(1)O(1)O(1)exp.O(1)

n 表示顶点数,m 表示边数, d v d_v dv 表示顶点 v 的度。注意邻接矩阵使用 O( n 2 n^2 n2) 的空间,而所有其他的结构使用 O(n+m) 的空间。

2.1 边列表结构

最简单但不是最有效的结构,所有顶点存储在一个无序的列表 V 中,所有边对象存储在一个无序的列表 E 中。

在这里插入图片描述

使用空间的大小是 O(n+m)。边列表最显著的局限性,是 get_edge(u,v)、degree(v)、和 incident_edges(v) 方法的运行时间为 O(m)。问题出在图的所有边在无序列表 E 中,能响应那些查询的唯一方法是通过对所有边进行详细的排查。

2.2 邻接列表结构

对每个顶点 v 维持一个集合 l(v),该集合被称为 v 的 入射 集合,其中全部是入射到 v 的边。(在有向图的情况下,输出边和输入边分别存储在两个单独的集合 lout(v) 和 lin(v)中。)

在这里插入图片描述

使用空间的大小是 O(n+m)。邻接列表的主要好处是集合 l(v) 正好包含那些应该用 incident_edges(v) 方法报告的边。因此,我们可以通过在 O(deg(v))时间内对 l(v) 的边进行迭代来实现这种方法,其中 deg(v)/ d v d_v dv 是顶点 v 的度。

邻接列表的局限性为 get_edge(u,v) 最好的实现需要 O(min( d u d_u du, d v d_v dv)) 的时间,因为我们必须在 l(u) 或者 l(v) 中搜寻。

2.3 邻接图结构

在邻接列表的基础上,我们可以使用基于哈希的映射为每个顶点 v 实现 l(v) 来提高性能。让每个入射边的另一个端点作为图的主键,用边结构作为值。

在这里插入图片描述

邻接图的空间使用仍为 O(n+m)。相对于邻接列表,邻接图的优势是方法 get_edge(u,v) 可以通过将顶点 u 作为关键字在 l(v) 中搜索以达到在预期时间 O(1) 内实现。

邻接图本质上对所有方法实现了最佳的运行时间,成为图表示中一种优秀的通用选择。

2.4 邻接矩阵结构

在邻接矩阵表达方式中,我们考虑顶点以集合 { 0 , 1 , ⋯   , n − 1 } \{ 0,1, \cdots ,n-1\} {0,1,,n1} 中的数字来表示,边以这些数字中的其中一对来表示。在二维数组 A 的单元格 A[i,j] 内存储边 (u,v) 的引用(如果边存在),其中 u 是索引为 i 的顶点,v 是索引为 j 的顶点。如果没有这样的边,那么 A[i,j]=None。

在这里插入图片描述

邻接矩阵最显著的优点是任何边 (u,v) 可以在最坏情况下的 O(1) 时间内被访问到(邻接图只支持在 O(1) 的预期时间内)。

邻接图的缺点:(1)邻接矩阵的使用空间为 O( n 2 n^2 n2),通常远差于其他表示方法,仅适用于密集图。(2)寻找入射到顶点 v 的边、添加或删除顶点等操作效率较低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值