【线性平面图判定算法】

最近去看了Hopcroft和Tarjan老爷子在1974年提出的 O(V)O(V)O(V) 时间内判断一个图是否是平面图的算法,这是原文。用了好一些时间才完全理解整个算法。想懂之后其实并不难,但真真实实非常巧妙,代码实现并不复杂,常数也不算大。不知道为什么网上基本找不到科普文。一些记号简单路径:每个点至多被经过一次。环:除起点外,每个点至多被经过一次。割点:删掉该点后原图不再连通。双连通图:连通且不包含割点的图。p:v⇒∗wp:v\Rightarrow ^* wp:v⇒∗w:ppp 是一条从
摘要由CSDN通过智能技术生成

由于离散课需要回课,就去学了Hopcroft和Tarjan老爷子在1974年提出的 O ( V ) O(V) O(V) 时间内判断一个图是否是平面图的算法,然后上课的时候讲。原文是 John Hopcroft and Robert Tarjan. 1974. Efficient Planarity Testing.J. ACM21, 4 (October 1974), 549-568. 这是链接

用了好一些时间才完全理解整个算法。想懂之后其实并不难,但真真实实非常巧妙。

感觉论文读起来困难是因为一开始没有把握到算法的整体思想,所以一度看的怀疑人生。一旦知道文章在干嘛,很多地方读起来就十分自然了。

课上听某同学讲,某年清华集训貌似就出了一道平面图判定的模板题,也不清楚标算是不是用的这个算法。

一些记号

简单路径:每个点至多被经过一次。

环:除起点外,每个点至多被经过一次。

割点:删掉该点后原图不再连通。

双连通图:连通且不包含割点的图。

p : v ⇒ ∗ w p:v\Rightarrow ^* w p:vw p p p 是一条从 v v v w w w 的路径。

树:根为 r r r 的有向图 T T T,从 r r r 出发可以到达所有点, r r r 没有入边,其余每个点恰好有一条入边。

v → w v\to w vw T T T 中一条 v v v w w w 的边,称 v v v w w w 的父亲, w w w v v v 的儿子。

v → ∗ w v\to ^* w vw T T T v v v w w w 的路径,称 v v v w w w 的祖先, w w w v v v 的后代。

d f s dfs dfs

G G G 是一个连通无向图。从一个点开始对 G G G d f s dfs dfs,令经过的边为树边,得到 G G G 的生成树 T T T,称 T T T G G G d f s dfs dfs 树。

若边 ( v , w ) (v,w) (v,w) 不是树边,且 v v v w w w T T T 中有祖先关系,不妨设 w w w v v v 的祖先,则称 ( v , w ) (v,w) (v,w) 为一条返祖边,记为 v − → w v-\to w vw

因为是通过 d f s dfs dfs 遍历图 G G G,所以 G G G 中的每一条非树边必然是返祖边。

d f n ( x ) dfn(x) dfn(x) 表示 x x x 被到达的时刻, l o w ( x ) low(x) low(x) 表示通过 x x x 的子树中的返祖边能到达的节点的 d f n dfn dfn 的最小值,形式化定义就是 l o w ( x ) = min ⁡ { d f n ( x ) , min ⁡ ( u , v ) ∈ E f , u ∈ s u b t r e e ( x ) d f n ( v ) } low(x)=\min\{dfn(x),\min_{(u,v)\in E_f,u\in subtree(x)}dfn(v)\} low(x)=min{ dfn(x),(u,v)Ef,usubtree(x)mindfn(v)}

其中 E f E_f Ef 表示返祖边构成的集合。同时令 l o w 2 ( x ) low2(x) low2(x) 表示次小值。

算法思想

对于一个连通图 G G G,它是平面图当且仅当它的每一个点双连通分量均为平面图。因此只需要考虑一个双连通图的平面图判定问题。

先找出 G G G d f s dfs dfs T T T,将每个点 x x x 重标号为 d f n ( x ) dfn(x) dfn(x),并将边重定向:令树边从父亲指向儿子,返祖边从后代指向祖先。找到 G G G 中的一个环 c : 1 → v 1 → v 2 → ⋯ → v n − → 1 c:1\to v_1\to v_2\to \cdots\to v_n-\to 1 c:1v1v2vn1,环中只有最后一条边是返祖边。删掉 c c c 后会得到很多个连通块,称之为 segment。每个 segment 要么是一条 v i → ∗ v j v_i\to^*v_j vivj 的返祖边,要么由某个 v i v_i vi 的儿子 u u u 的子树中的所有边(包括返祖边)组成。则每个 segment 最后必然全落在 c c c 的内侧或外侧。

每个 segment 都对应一个环 c u : v i → u ⇒ ∗ l o w ( u ) → ∗ v i c_u:v_i\to u\Rightarrow ^*low(u)\to ^*v_i cu:viulow(u)vi,可将 c u c_u cu 看作该 segment 的外轮廓。按照从外向内的顺序嵌入 segment,则该 segment 的其他部分可被嵌入到 c u c_u cu 的内部,不影响其他已经被嵌入的 segment。

对于一个新加入的 segment,先考虑在已经嵌入了环 c c c 和前面被加入的 segment 的基础上,其外轮廓能否被嵌入到平面中。若不能嵌入,则 G G G 不是平面图。否则保留 c u c_u c

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 全零数组的判定是指判断一个数组中是否全部元素都为零的问题。对于一个数组,可以通过遍历数组的每个元素,判断是否为零来解决这个问题。但是这种方法的时间复杂度是O(n),其中n是数组的长度。在面对非常大的数组时,这种方法的效率较低。 针对全零数组的判定问题,可以采用亚线性算法进行优化。亚线性算法是指效率超过线性时间复杂度O(n)的算法。在Python中可以使用numpy库提供的优化方法来实现亚线性算法。 具体实现步骤如下: 1. 导入numpy库:import numpy as np 2. 定义一个数组arr,假设数组长度为n。 3. 使用numpy库的函数np.any()判断数组arr中是否存在非零元素。该函数会遍历数组arr,一旦遇到非零元素,就返回True;如果数组中全部元素都为零,就返回False。 4. 判断np.any(arr)的返回值,如果为True,则说明数组arr不全为零;如果为False,则说明数组arr全为零。 使用numpy库的np.any()函数进行全零数组判定的亚线性算法的时间复杂度为O(1),效率较高。通过这种方法可以快速判断一个数组中是否全部元素都为零。 ### 回答2: 全零数组的判定是指判断一个给定数组是否全部为零。在Python中,可以使用亚线性算法来实现这个判定。 亚线性算法是指算法的时间复杂度不是严格的线性时间复杂度,但是比线性时间复杂度更快的算法。对于全零数组的判定,可以通过以下步骤实现亚线性算法: 1. 首先,遍历给定的数组,计算数组的长度n。 2. 定义一个变量count,用于记录非零元素的个数。 3. 遍历数组,如果遇到非零元素,则count加1。 4. 如果count大于0,则说明数组中存在非零元素,即该数组不是全零数组,返回False。 5. 如果count等于0,则说明数组中不存在非零元素,即该数组是全零数组,返回True。 在这个算法中,遍历数组的时间复杂度为O(n),其中n为数组的长度。由于只需要遍历一次数组,因此这个算法的时间复杂度是亚线性的。 下面是用Python代码实现这个亚线性算法的示例: def is_all_zeros(arr): n = len(arr) count = 0 for i in range(n): if arr[i] != 0: count += 1 if count > 0: return False else: return True arr = [0, 0, 0, 0] print(is_all_zeros(arr)) # 输出True arr = [1, 0, 0, 0] print(is_all_zeros(arr)) # 输出False 通过以上算法,我们可以快速判断一个给定的数组是否为全零数组。 ### 回答3: 全零数组的判定是指判断一个数组中的所有元素是否全部为零。在Python中,可以使用亚线性(sublinear)算法来判断全零数组。 亚线性算法是指算法的运行时间小于线性时间,但大于常数时间。对于全零数组的判定,一个常规的做法是逐个遍历数组中的元素,如果遇到非零元素则返回False,如果遍历完所有元素都为零则返回True。这种做法的时间复杂度为O(n),其中n是数组的长度。 而亚线性算法可以在更短的时间内完成全零数组的判定。一种常见的亚线性算法是使用位运算。具体做法是将数组元素逐个取反,并使用逻辑与运算符(&)将所有的元素进行与运算。如果最终结果为0,则表示原数组中的所有元素均为零,返回True;否则返回False。这种亚线性算法的时间复杂度与数组的长度无关,通常可以在常数时间内完成判定。 下面是使用亚线性算法判定全零数组的Python代码示例: ``` python def is_all_zeros(arr): result = 0 for num in arr: result |= num # 将数组元素逐个取反并使用逻辑或运算符进行合并 return result == 0 # 示例测试 arr1 = [0, 0, 0, 0] arr2 = [0, 1, 0, 0] print(is_all_zeros(arr1)) # 输出 True print(is_all_zeros(arr2)) # 输出 False ``` 这个算法的时间复杂度是O(1),因为无论数组的长度如何,都只需要进行一次遍历和一次位运算即可完成判定,效率非常高。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值