在树结构中合并分支的模式或算法?

时间:2013-04-10 14:47:36

标签: algorithm design-patterns

我正在尝试将DAG(有向无环图)映射到我在下面显示的结构中。

以下是我从

开始的DAG示例

enter image description here

其中弧总是从左到右。

然后我恢复图形并将其跨越到具有如下重复节点的树中:

enter image description here

我正在寻找的是一些算法或模式来实现以下合并结构。 (注意它会再次被还原)

enter image description here

目标是生成这样的XML:

<root>
    <seq>
        <mod1/>
        <flow>
            <seq>
                <mod4/>
                <mod7/>
            </seq>
            <seq>
                <flow>
                    <seq>
                        <flow>
                            <mod4/>
                            <mod3/>
                        </flow>
                        <mod6/>
                    </seq>
                    <seq>
                        <flow>
                            <mod4/>
                            <mod3/>
                            <mod2/>
                        </flow>
                        <mod5/>
                    </seq>
                </flow>
                <mod8/>
            </seq>
        </flow>
    </seq>
</root>

我认为它不相关,但我正在解析JSON以使用JAVA 7编写XML。 框是Web服务,箭头表示输入和输出参数,因此,例如,模块1在模块1,2,3和4完成后调用,其输出是其输入。

编辑:好的,这是另一个包含十个节点的示例。我希望这能让您更好地了解何时合并节点。

enter image description here

要回答@blubb,在这个例子中我们可以看到“服务”8&amp; 9也已合并。否则他们需要工作的所有服务(1,2,3,4,5和6)将被呼叫两次而不需要。最后一个草图中的中间分支将执行两次:一次为8次,一次为9次。

2 个答案:

答案 0 :(得分:1)

我不太了解树数据结构,我想这可能是结果的良好外壳,而且我对XML的转换知之甚少,但是如果我给出了路由映射的数据例如,

1 4 7
1 2 5 8
1 3 5 8
1 4 5 8
1 3 6 8
1 2 5 9
1 3 5 9
1 4 5 9
1 3 6 9
1 2 10
1 2 5 10
1 3 5 10
1 4 5 10

然后合并节点的一种方法可能是:

Take increasingly larger chunks from the end of each line and examine the
first cell to the left of them. Nodes are merged if matching right-side 
chunks flow to the same aggregated first cells on the left. Remove duplicate 
paths.


说明/示例:


第一次传递(取结束单元格,与其左侧聚合的第一个单元格进行比较):

  4   <- 7
  5,6 <- 8
  5,6 <- 9
  2,5 <- 10

唯一可以合并的节点是8和9,因为它们都流向相同的聚合单元格(5,6)。

首次通过的结果:

1 4 7
1 2 5 (8,9) -- merged
1 3 5 (8,9)
1 4 5 (8,9)
1 3 6 (8,9)
1 2 5 (8,9)
1 3 5 (8,9)
1 4 5 (8,9)
1 3 6 (8,9)
1 2 10
1 2 5 10
1 3 5 10
1 4 5 10


第二次传递(取结束单元格+ 1个单元格,与左侧聚合的第一个单元格进行比较):

  1      <- 4 7
  2,3,4  <- 5 (8,9)
  3      <- 6 (8,9)
  1      <- 2 10
  2,3,4  <- 5 10

没有可以合并,因为没有匹配的右侧路径流向左侧相同的聚合第一个单元格。


第三次传递(取结束细胞+2个细胞,与左侧聚合的第一个细胞进行比较):

  N/A    <- 1 4 7
  1      <- 2 5 (8,9)
  1      <- 3 5 (8,9)
  1      <- 4 5 (8,9)
  1      <- 3 6 (8,9)
  N/A    <- 1 2 10
  1      <- 2 5 10
  1      <- 3 5 10
  1      <- 4 5 10

可以进行两次合并。首先:[2 5(8,9)],[3 5(8,9)]和[4 5(8,9)]全部流向1.其次:[2 5 10],[3 5 10] ,[4 5 10]全部流向1。

第三遍结果:

1 4 7
1 (2,3,4) 5 (8,9)
1 3 6 (8,9)
1 2 10
1 (2,3,4) 5 10

看起来很像我要求的结果。 (两端的重复单元可以合并为单个节点,即左侧为1,右侧为(8,9)和10,如eskalera的最终草图。)

答案 1 :(得分:1)

最后,我找到了一个完成这项工作的算法。这是适合所有试图帮助我的人:

首先,我在草图1中从DAG构建了一个倒置的生成树。所以我从模块7和8开始,向后构建树以及复制模块。

之后,我创建名为FLOW和SEQUENCE的虚拟节点,并在树中引入它们,以便每个MODULE节点都是SEQUENCE节点的子节点。生成分支是SEQUENCE节点,它们是FLOW节点的子节点。我认为这个步骤足够直观,但重要的是要理解我们需要虚拟节点,这样我们就可以关闭FLOW节点,这些节点是从一个节点分裂到多个节点的。

之后我首先回顾树深度,对于每个模块(我们称之为驱动程序),我将它的孩子与司机兄弟姐妹的孩子进行比较。如果它们不匹配,我会继续与驾驶员的兄弟姐妹的孙子一起下来,这样,从驾驶员的兄弟姐妹中出来的所有分支必须通过与驾驶员相同的节点。从图形上看,这意味着在某些时候,两个节点都需要完全相同的模块。

如果匹配,我会从重合的节点向下清理合并的分支,这意味着我将它们从父母身上切掉。从那里开始,它与驱动程序SEQUENCE节点一起进入一个新的SEQUENCE节点,进入同一个FLOW节点。

在遍历整棵树之后,只要合并完成,我们就会再次遍历树,这次是一段更大的关系。这意味着我们不是比较驾驶员的孩子,而是比较驾驶员的好孩子。

最后一步显然是要再次恢复树。

由于这些虚拟节点的编程意味着错综复杂,我遗留了一些概念。主要是由于一旦引入了虚拟节点,所有父子关系都会丢失。但我希望大家的想法得到了解。