【问题标题】:Algorithm: Identifying Duplicate Child Sets Across Levels of Tree算法:跨树层识别重复的子集
【发布时间】:2013-10-08 09:35:46
【问题描述】:

假设我有以下内容:

   _____W_____
  |     |     |
 _T_   _L_   _X_
|   | |   | |   |
A   B A   B A   B

如您所见,它是一棵标准树(不是二叉树,W 有三个孩子就证明了这一点)。我的目标是确定A B 子序列在整个底层重复的事实。

更一般地说,我希望能够从树的根部开始,查看我孩子的子子树集(本质上是孙子树集)并确定它们是否一直相同跨树级别,然后递归到我的孩子并在每个较小的范围内做同样的事情。冲洗,重复,一直到整个树的底部。

我想到的一个简单的解决方案是对每个子树进行广度优先(或深度优先)遍历(在这种情况下,TLX)和比较我想出的单词(减去第一个字符)。在这种情况下,广度优先遍历将产生TABLABXAB,忽略第一个字符,我会看到它们都是AB。但想象一下,如果这棵树是以下内容:

   _____W_____
  |     |     |
 _T_   _L_   _X_
|   | |   | |   |
A   B Q   B A   B

能够获取第一个A,然后是Q,这样会更有效率,意识到它们是不一样的,继续搜索和短路是没有意义的。

我主要是想看看是否有一些“明显”的算法可以在这里应用,或者,也许是针对这个特定问题创建的算法;我从未见过、找不到和/或不知道如何搜索。

(我还用“Java”标签标记了这个问题,仅仅是因为我对这个树形结构的实际实现[以及我应用到它的其他算法并且 not 有未回答的问题] 发生用那种语言。我也可以翻译伪代码。)

编辑 - 作为上面第一棵树上的一些示例步骤,这可能更有意义:

  • W(根)开始。
  • 我有 2 个或更多孩子吗?在这种情况下,是的,3:TLX
  • 比较TLX 的子子树。
  • TLX 的子子树集在 整个 级别W 的范围内是否相同?在这种情况下,是的,它一直是A B。在上面的第二棵树中,答案是否定的,因为 Q 把事情搞砸了。
  • 现在下拉至W 的孩子TLX。从上面重复前面的步骤。 T 有 2 个或更多孩子吗?是的,AB。他们有孩子吗?在上面的例子中,没有,所以没有什么可做的。但是想象AB 是完整的子树,有孩子、孙子等等。现在的问题是:这些子树在整个级别上是否相同T 的范围?那么A son of T 的子子树集是否与B son of T 的子子树集相同?

【问题讨论】:

  • 帮助我理解你所追求的概括。您是否总是想比较第 3 级的元素?根的所有子节点的叶子集?还是别的什么?
  • @jacobm 添加了一个编辑(希望)更清晰的想法。
  • 所以要明确一点:您似乎想确定 W 的所有直接子代是否相同,除了它们的最顶层标签。那正确吗?例如,如果A 是一棵大树而不仅仅是一片叶子,那么您绘制的树中A 的每个外观都需要具有完全相同的形状和标签?
  • @jacobm 是的,我想这可能是最短的解释:从根(这里,W)开始,我的所有直接子子树的形状和标签都相同,除了他们根上的标签?下到我的直系子女(这里,TLX),问他们每个人同样的问题。然后他们的孩子等等,本质上是广度优先遍历,在每个节点问这个问题。
  • 考虑在cs.stackexchange.com问cs相关的问题。

标签: java algorithm tree


【解决方案1】:

注意:声称短路相等性检查比需要测试的枚举策略“更有效”。如果您的输入集不是很大,则不太可能产生影响,如果它很大,那么您可能需要使用代表性数据进行测量。

也就是说,这里有一个算法的伪代码,它从左到右比较所有子树,尝试在树中一次查看一个元素,而不是预先生成所有集合:

function AllLeavesEqual(tree):
  if (tree.children.size < 2):
    return true
  subtreeIterators = [GetLeafIterator(t) for subtree in tree.children]
  baseLeaves = subtreeIterators[0]
  comparisonLeaves = subtreeIterators[1:]
  pop one item off of each iterator
  while (baseLeaves.hasNext()):
    nextLeaf = baseLeaves.next()
    for comparisonIterator in comparisonLeaves:
      if (!comparisonIterator.hasNext() or comparisonIterator.next() != nextLeaf):
        return false

  return true iff no iterator in comparisonLeaves satisfies iterator.hasNext()

function GetLabelIterator(tree):
  return Iterator:
    stack = Stack(tree)

    define next():
      t = Pop(stack)
      push each of t.children onto stack in reverse order
      return t.label

    define isEmpty():
      return stack.isEmpty()

我在这里所做的只是检查每个子树中的每个标签是否相等,诀窍是我使用迭代器而不是具体化标签集,它有效地执行每个子树的前序遍历懒惰。您当然可以使用任何其他您想要的惰性树节点枚举方法。

请注意两点:首先,此遍历不是您想要的级别顺序遍历。而是先序遍历;如果使用级别顺序遍历真的很重要,那么您需要将我上面编写的迭代器替换为以这种方式枚举的迭代器。其次,如上所述,该算法不检查结构相等性,仅检查有序遍历相等性。如果重要,这很容易解决。

【讨论】:

  • 我不认为子树的遍历顺序实际上很重要;无论您从子树中的哪个节点开始,在遍历中的任何点遇到不匹配的几率在技术上都应该是相同的。唯一的问题是确保选择的遍历不会比较子树的根,您的伪代码会在开始比较之前将它们弹出来。我不会想到使用迭代器——这是对至少在 Java 中被认为是相当古老的对象/模式的巧妙使用。我明天试试这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2020-12-20
  • 2019-01-19
  • 2015-10-18
  • 1970-01-01
相关资源
最近更新 更多