Algorithm 面的邻接或边列表

Algorithm 面的邻接或边列表,algorithm,graph,polygons,face,planar-graph,Algorithm,Graph,Polygons,Face,Planar Graph,如何从平面图的边列表或邻接列表表示转到面列表 例如,对于这个图(奇怪的是,它没有0索引): 我想要一个如下所示的列表: [ [1,2,8,7], [1,2,4,3], [1,3,5,7], [2,4,6,8], [5,6,7,8], [3,4,6], [3,5,6] ] 它不必是那种格式或顺序,但它应该是所有面的某种列表(或集合)。包括外表面 对于有V个顶点的图(E=O(V),因为是平面的),算法应该在O(V)中生成此列表。简单的回答是:您必须实际布局图!更确切地说,你必须在平面中找到一个图

如何从平面图的边列表或邻接列表表示转到面列表

例如,对于这个图(奇怪的是,它没有0索引):

我想要一个如下所示的列表:

[
[1,2,8,7],
[1,2,4,3],
[1,3,5,7],
[2,4,6,8],
[5,6,7,8],
[3,4,6],
[3,5,6]
]
它不必是那种格式或顺序,但它应该是所有面的某种列表(或集合)。包括外表面


对于有V个顶点的图(E=O(V),因为是平面的),算法应该在O(V)中生成此列表。

简单的回答是:您必须实际布局图!更确切地说,你必须在平面中找到一个图的嵌入——假设有一个图没有交叉边

因此,您上面的嵌入是:

1: [2, 7, 3]
2: [1, 4, 8]
3: [1, 5, 6, 4]
...
这是每个顶点在其相邻集上的顺序。您必须指定该顺序是顺时针还是逆时针,否则应全部指定

一旦你有了嵌入,就可以使用一个新的工具来恢复这些面。这看起来比实际情况更复杂,尽管它确实涉及飞镖(或旗帜)

首先,将每条边分解为标志(顶点+半边)并进行排列(wiki描述中的sigma)以存储地图。例如,我们可以按照与地图相同的顺序标记标志,然后1:[2,7,3]变成{1->2:1,1->7:2,1->3:3}等等

例如,立方体(注意:删除了中间边!):

然后计算alpha(对合置换),它只是将标志映射到边缘上的另一个标志。最后,φ是这两种排列的乘积,φ的循环给出了面


这样,从图像中的φ,我们有(1, 6, 24,19)是外表面(注意这些是飞镖,所以我们考虑它是从顶点开始的)。

< P> > GeLeAIn在他的回答中提到,你必须实际布局图,因为如果你只给出拓扑,就可以有多个布局。这里我将给出一些算法分析,然后给出一个简单的解决方案

引理1:一条边至少涉及一个面,最多涉及两个面 脸

证明:我们在二维空间中绘制,所以一条线将平面分成两半

这意味着我们可以在每个边上附加一个“容量”,初始化为2。当我们找到一个包含边的面时,我们从中减去1

由于面在平面图中是一个循环,因此在给定上述约束的情况下,问题转向寻找循环

引理2:如果我们检查两个有效解之间的差异,它们在具有互补能力的边上不同(1对2和2对1)

证明:参见图。

因此,当您在查找解决方案时耗尽边的容量时,您会自动排除导致另一个解决方案的模糊性

因此,简单的解决方案是在图中执行DFS以查找循环。找到一条边后,减去相关边的容量。当容量达到0时,考虑DFS进一步移除边沿。strong>注意,当发现一个循环时,我们必须检查其中的所有边是否具有容量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),因为每条边消耗不超过两次。

您需要生成图形的平面嵌入。一个问题是,通常一个双连通图可以生成多个平面嵌入

文中给出了一种平面性测试和嵌入算法,解决了在O(V+E)时间和内存中生成图的所有可能的平面嵌入的问题(对于具有V个顶点和E个边的双连通图,在O(p(V+E))时间和O(V+E)内存中)内存以生成所有可能的唯一平面嵌入,其中P是嵌入的排列数)

第5章详细介绍了测试图的平面性,然后为每个顶点生成循环边顺序所需的算法(以及如何迭代到嵌入的下一个排列)


给定循环边顺序,当您到达每个连续顶点时,可以通过循环边顺序中的每条边和下一条顺时针(或逆时针)边生成图形的面。

邻接列表是否按角度排序?如果不是,可能是吗?有趣的答案。我对图表不太清楚-颜色是什么意思?还有-也许你知道这一点,但是-在答案正文中,在用户名前加“@”没有任何作用:)@gilleain是的,我只是希望有一天他们会在答案文本中添加“@”功能。在图中,绿色=面1的边,红色=面2的边。对于中间的图形,这两个面似乎是重叠的,但是通过调整布局(将底部顶点移到顶部),我们可以看到这仍然是一个有效的解决方案——我认为这是为什么这个问题有趣的一部分:)通过DFS查找循环并不等同于寻找人脸。可以在DFS中找到一个循环,然后将定义在此循环中的区域由DFS中的其他路径平分,这样面实际上是由原始循环和平分定义的两个子区域。这可以用连续的子区域重复。@MT0请参见引理2。虽然我们可以计算更大的外部循环,但该算法确保它不会进一步计算更大循环内的所有循环。因此,通过调整布局,我们使较大的周期变小,较小的周期变大,使其看起来像两个不相交的面。请在您的解释中详细说明,因为它目前似乎没有意义,并且不清楚在您的“引理2”中证明了什么。您似乎严重依赖DFS处理边缘的顺序-如果这是不确定的,则您的算法无法在常规ca中工作