如何从平面图的边列表或邻接列表表示转到面列表?
例如,使用此图表(不是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 :(得分:2)
您需要生成图形的平面嵌入。一个问题是双连图通常可以生成多个平面嵌入。
在"Planarity Testing by Path Addition" PhD thesis中给出了一个平面度测试和嵌入算法,这解决了生成图形的所有可能的平面嵌入的问题(在O(V + E)时间和存储器中用于单个嵌入的双连接具有V顶点和E边的图,以及在O(P(V + E))时间和O(V + E)存储器中生成所有可能的唯一平面嵌入,其中P是嵌入的排列数。)
第5章详细介绍了测试平面度图表然后为每个顶点生成循环边缘顺序所需的算法(以及如何迭代到嵌入的下一个排列)。
给定循环边缘顺序,您可以通过获取每个边缘并在循环边缘顺序中跟随下一个顺时针(或逆时针)边缘来生成图形的面。当您到达每个连续的顶点时。
答案 1 :(得分:1)
简短的回答是:你必须实际布局图表!更确切地说,你必须在平面中找到图形的嵌入 - 假设有一个没有边缘交叉。
所以,你上面的嵌入是:
1: [2, 7, 3]
2: [1, 4, 8]
3: [1, 5, 6, 4]
...
这是每个顶点在其邻居集上具有排序。您必须指定该顺序是顺时针还是逆时针,否则应该全部。
嵌入后,可以使用combinatorial map恢复面部。这看起来比实际上更棘手,虽然它确实涉及飞镖(或旗帜)。
首先,将每个边缘分成标志(顶点+半边缘)并进行存储地图的排列(维基描述中的西格玛)。例如,我们可以按照与地图相同的顺序标记标志 - 然后1:[2,7,3]变为{1-> 2:1,1-> 7:2,1-> 3: 3}等等。
例如,一个立方体( note :删除了中间边缘!):
然后计算alpha(对合排列),它只是将标志映射到边缘的另一个标志。最后,phi是这两种排列的产物,phi的循环为你提供了面孔。
所以,从图像中的phi,我们有(1,6,24,19)这是外面的(注意这些是飞镖,所以我们考虑从它开始的顶点)。
答案 2 :(得分:0)
正如@gilleain在他的回答中提到的,你必须实际布局图形,因为如果你只给出拓扑结构可能有多个布局。在这里,我将给出一些算法分析,然后是一个简单的解决方案。
引理1:边缘涉及至少一个面,最多两个面 面。
证明:我们在2D空间中进行绘制,因此一条线将一个平面分成两半。
这意味着我们可以附加一个"容量"每个边缘,初始化为2.当我们找到一个涉及边的面时,我们从中减去1。
由于face是平面图中的一个循环,因此在上述约束条件下问题转向查找循环。
引理2:如果我们检查两个有效解之间的差异,它们在具有互补能力的边缘上是不同的(1对2和2对1)。
因此,当您在找到解决方案时消耗边缘容量时,会自动排除导致另一种解决方案的歧义。
因此,直接的解决方案是在图中进行DFS以找到循环。找到一个时,减去所涉及边的容量。当容量达到0时,请考虑在DFS进一步移除边缘时。 注意,当找到循环时,我们必须检查其中的所有边是否具有容量1.如果是,则必须跳过此循环,因为将此循环包含在我们的结果中会导致重复计数。 < / p>
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),因为每个边消耗不超过两次。