1: [2, 7, 3]
2: [1, 4, 8]
3: [1, 5, 6, 4]
...
这是每个顶点都有其邻居集的排序。您必须指定该排序是顺时针还是逆时针,但除此之外就没有其他要求了。
一旦您完成嵌入,就可以使用组合地图来恢复面。这看起来比实际情况要棘手,尽管它涉及到飞镖(或标志)。
首先,将每个边缘分成标志(顶点+半边),并创建一个存储地图的排列(维基描述中的sigma)。例如,我们可以按照地图的相同顺序标记标志 - 然后1:[2、7、3]变为{1->2:1、1->7:2、1->3:3}等等。
例如,一个立方体(注意:删除了中间的边缘!):
然后计算alpha(反演置换),它仅将标志映射到边缘另一侧的标志。最后,phi是这两个置换的乘积,phi的循环给出面。因此,从图像中的phi,我们得到(1, 6, 24, 19),这是外部面(请注意,这些是darts,因此我们考虑它开始的顶点)。正如@gilleain在他的回答中提到的,您必须实际布局图形,因为如果您只给定拓扑结构,则可能存在多个布局。在这里,我将进行一些算法分析,然后给出一个简单的解决方案。
引理1:每条边涉及至少一个面,最多涉及两个面。
证明:我们在二维空间中绘图,所以一条线将平面分成两半。
这意味着我们可以为每条边附加一个“容量”,初始值为2。当我们发现涉及一条边的面时,从该容量中减去1。
由于面是平面图中的循环,因此问题转化为在给定上述约束条件的情况下找到循环。
引理2:如果我们检查两个有效解之间的差异,则它们在具有互补容量(1 vs. 2和2 vs. 1)的边上不同。
因此,当你在寻找解决方案时排除了导致另一个解决方案的歧义,就会耗尽边缘的容量。
因此,直接的解决方案是在图中进行深度优先搜索以查找循环。当您找到一个循环时,请减去涉及边缘的容量。当容量达到0时,在DFS进一步时考虑删除该边缘。请注意,当发现一个循环时,我们必须检查其中所有边缘是否具有容量1。如果是这样,则必须跳过此循环,因为将此循环包含在我们的结果中会导致重复计数。
def DFS-Tarjan(v, capacities, stack)
for e in N(v):
if capacity[e] != 0:
if stack.contains[e.target] AND NOT all of them have capacity 1:
output-loop(stack, e.target)
for e2 in stack:
capacity[e2] -= 1
else:
stack.push(e.target)
DFS-Tarjan(v, capacities, stack)
stack.pop(e.target)
else:
pass # capacity drained
def find-faces(g):
initialize capacities of g.E to [2...2]
for v in unvisited(g.V):
DFS-Tarjan(v, capacities, [])
如果您更改DFS的顺序,则可以找到多个解决方案。对于单个解决方案,该算法的时间复杂度为O(V),因为每条边最多只被使用两次。
AND NOT all of them have capacity 1:
,我认为最好只保留核心逻辑并剥离细节。 - Yadlie.target
是伪代码...好的,但它代表什么?它是相邻顶点吗?它是DFS树中的祖先吗?还是其他什么?目前,我被你的伪代码缺乏清晰度打败了,无法开始实现你的算法以正确验证它。 - MT0