如何在无向图中找到所有多边形?

时间:2014-08-15 23:25:02

标签: algorithm graph graph-theory

给定一个无向图,在这样的图中找到所有多边形的算法是什么?这是一个彩色多边形的示例图。 example graph

请注意,有一个多边形ABCIHGJKLMLKA,其中包含节点KLM,但多边形CDEG不包含F.

我已经阅读了这个问题的解决方案,但没有我的叶子要求。先前解决方案中存在的一些公理是每个边缘仅使用两次,但是死端边缘总共需要遍历四次。也就是说,存在一个包含所有外部节点ABCDEFGJKLMLKA的多边形,但是它会被丢弃,因为它会朝外。

对于类似问题的一个解决方案,没有叶子,在这里描述:http://blog.reactoweb.com/2012/04/algorithm-101-finding-all-polygons-in-an-undirected-graph/

更新

似乎链接的解决方案不能按预期工作,例如:

problem

该算法将遍历图A-B-C-A-E-D-C,识别三角形ABC,还有多边形CAEDC,这不是意图

UPDATE2

实际上有一个简单的解决方案:删除包含其他多边形点的较大多边形。

2 个答案:

答案 0 :(得分:1)

step | description
1a   | while vertices with deg(v) = 0 exist
1b   |    mark vertices with deg(v) = 0 as leaf
     | 
2    | run algorithm on all vertices which are not marked as leaf
     | 
3a   | for each vertex marked as leaf 
3b   |    if vertex is inside a polygon
3c   |       check its edges // you have to decide what to do in which case
3d   |       adjust polygon

我将用你的例子来说明这一点:

step | result
1a   | find F and M
1b   |   mark F and M as leaf
1a   | find L
1b   |   mark L as leaf
1a   | find nothing: go to step 2
     |
2    | finds polygons: AKJGHICB (1), CIHG (2), and CGED (3)
     |
3a   | we have F, M, and L
3b   |   check F: 
     |     poly (1): cast ray: even result -> outside
     |     poly (2): cast ray: even result -> outside
     |     poly (3): cast ray: even result -> outside
     |     since F is always outside: no further action needed, unmark F
3b*  |   check M:
     |     poly (1): cast ray: odd result -> inside
     |     since M is inside a polygon: check how to add it
3c   |   check edge M-L:
     |     check if L is part of poly (1)
     |       if yes: add path to poly (1) (step 3d)
     |       if no: check if L is inside poly (1)
     |       -> no check L: odd result -> inside
     |         if inside: follow path, i.e. step 3c with edge L-K
     |         if outside: undefined behaviour
     |           -> inside
3c   |   check edge L-K:
     |     check if K is part of poly (1)
     |       -> yes: add path to poly
3d   |   Take poly (1) AKJGHICB
     |     replace K with KLK
     |     unmark K // note that K was never marked)
     |     remove K from path
     |     replace L with LML
     |     unmark L
     |     remove L from path
     |     unmark M // note that you should check if there are more
     |              // vertices to come for the replacement
     |     remove M from path 
     |   poly (1) is now AKLMLKJGHICB
3a   | we have no marked vertices left
     | finish


* note that in step 3b we could first have found L/checked L. Then it would be like this:

3b   |   check L:
     |     poly (1): cast ray: odd result -> inside
     |     since L is inside a polygon: check how to add it
3c   |   check L-K (or M-L, that would work as above and eventually try L-K)
     |     check if K is part of poly (1)
     |     if yes: add path to poly (1)
     |     -> yes
3d   |   Take poly (1) AKJGHICB
     |     replace K with KLK
     |     unmark K
     |     remove K from path
     |     unmark L
     |     remove L from path
     |   poly (1) is now AKLKJGHICB
3a   | we have M left // from now on a bit less detailed because it's the same again
3b   |   check M:
     |     poly (1): cast ray: odd result -> inside
     |   ...
3c   |   check M-L
     |     L is part of poly (1)
3d   |   replace L in the poly with LML and unmark L and M
     | finish

这应该是一个粗略的想法,如何一个你已经熟悉的算法应该工作。但是,它可能会有很多改进。

答案 1 :(得分:0)

Re AndreyT建议使用DCEL:双连接边缘列表表示的显着特征是,对于每个无向边缘,有两个列表节点,每个方向一个。我们将这些节点称为 darts ,并将它们视为具有头部和尾部。给定飞镖(例如,H-> G,具有尾部H和头部G),我们可以以逆时针顺序找到具有相同头部的反向飞镖(例如,G-> H)和下一个飞镖(例如,J) - > G)。给定一个可以用于按角度排序的基元,可以直接构造DCEL(最简单的方法是按atan2()排序;最好的方法是找到一个能够在面临浮点渎职时产生一致结果的行列式测试)

通过找到排列的排列周期可以找到多边形(通常称为面),该排列将每个飞镖以逆时针顺序将相同的头映射到下一个飞镖的反向。例如,如果我们从飞镖C-> D开始,那么我们遵循循环

C->D (E->D is next) D->E (G->E is next) E->G (C->G is next) G->C (D->C is next) C->D

并恢复面部C-D-E-G-C。从A-> B开始,我们得到

A->B B->C C->I I->H H->G G->J J->K K->L L->M M->L L->K K->A A->B,

是面部A-B-C-I-H-G-J-K-L-M-L-K-A。

此方法需要连接图。 (它将在断开连接的图形上工作,但它可能无法提供您想要的结果。)它还会产生无限的面,您指出这是不可取的。要在无限面上找到一个飞镖(可用于识别它),找到y坐标最小的顶点,用最少x坐标打破连接。然后从直线向右射击的逆时针方向找到那个头部的最后一个飞镖。