Java算法比较树的子树

时间:2013-11-22 23:43:04

标签: java algorithm tree comparison treenode

我正在寻找一种比较两棵树的算法,并检查on树是否是另一棵树的子树。

我首先提供了一个自己的树实现,它具有以下结构:

public class PlatformTree {
   private HwNode root;

   .......
}

public class HwNode {
  private HwNode parent;
  private ElementType elementType;
  private Hardware hw;
  private List<Node> children = new ArrayList<Node>();

  public Node(Node parent, ElementType elementType, Hardware hw) {
     ...
  }

  public void addChild(Node child) {
     children.add(child);
  }

  ....

以下图片应该为您提供概述:

树1

Tree1

Tree2

enter image description here

如图所示,我想检查Tree1中是否包含Tree1的所有子树。 树的根项只是虚拟元素,用于访问子树。

我总是检查Tree2中是否包含Tree1的子树。 Tree1类型的节点总是有一个后继(Microcontroller.Memory),而Tree2类型的节点可以有几个后继(Microcontroller.Memory / Memory / Memory)。

ElementType确定节点是否相等。

如果所有子树都包含在另一个树中,则比较方法应返回 True 。否则它应该返回 False 。我现在花了很长时间来提供算法,但我估计递归调用仍有一些问题。这就是我到目前为止所做的:

类TreePlatform:

public boolean containsSubTree(PlatformTree tree1) {
        Boolean b = true;

        // check all subtrees of Tree1
        if (tree1.getRoot() != null && getRoot() != null) {
            for (HwNode subRootNode : tree1.getRoot().getChildren()) {
                b = getRoot().containsSubTree(subRootNode);
            }
        } else {
            return false;
        }
        return b;
    }

班级HwNode

public boolean containsSubTree(HwNode abstractNode) {
    for (HwNode subRootNode : getChildren()) {
        if (hasSubTree(subRootNode, abstractNode)) {
            return true;
        }
    }
    return false;
}

private boolean hasSubTree(HwNode subRootNode, HwNode abstractNode) {
    if (subRootNode.getElementType() != abstractNode.getElementType()) {
        return false;
    }
    if (!abstractNode.getChildren().isEmpty()) {
        if (!subRootNode.getChildren().isEmpty()) {
            HwNode abstractSubNode = abstractNode.getChildren().get(0);
            for (HwNode subNode : subRootNode.getChildren()) {
                if (!hasSubTree(subNode, abstractSubNode)) {
                    return false;
                }
            }
        } else {
            return false;
        }
    } else if (subRootNode.getChildren().isEmpty()) {
        return true;
    }
    return true;
}

我对我的算法感到不满意,但此刻我仍然感到困惑,不知道如何解决这个问题。我有什么想法可以比较两棵树吗?

1 个答案:

答案 0 :(得分:3)

Aight,所以问题是在另一棵树中寻找一棵树。这是一个标准的面试问题,如果树有一个订单,但你的没有,这使事情变得更加复杂。类似地,如果我们正在寻找树同余而不是包含,并且如果我们可以使用Kelly's Theorem作为Adam Gent在OP的注释中指示并且只计算子树。

为了清楚起见,我打算将两棵树称为模式树目标树。我们想测试模式树是否是目标树的子树。

你的方法是(虽然我可能会理解)模式树中的节点P等同于目标树中的节点T iff P的每个子节点等于T中的节点。事实是,这有一个问题:如果P与子项具有两个相同的子树,该怎么办?它与只有一个子树的节点T匹配!

现在这不是一个致命的缺陷:我们可以通过在我们认为找到匹配时删除T的子树来调整这种方法,然后如果后来证明我们错了就进行一堆回溯。但是,这有点像组合爆炸。

更好的方法是从头开始而不是自上而下。我的意思是

  • 检查所有模式树的叶子是否出现在目标树中。
    • 如果他们这样做,建立一个字典D,将模式树的叶子映射到目标树中匹配叶子的列表(可能有多个!)。
  • 检查模式树的所有高度-1子树是否出现在目标树中。
    • 为此,我们假设模式树中有一个高度为1的节点Q,叶子为X和Y.
    • 使用字典获取目标树中与X和Y匹配的叶子列表。过滤这些列表,以便目标树中的任何叶子最多出现一次(这涉及X与Y相同的情况)< / LI>
    • 查看目标树中匹配X的某些叶子是否与目标树中与Y匹配的叶子具有共同点。
    • 如果是这样,万岁!如果这个父类型与Q相同,那就是匹配!找到所有这样的父母 - 我们下一步就需要它们。
  • 如果模式树的所有height-1子树确实出现在目标树中,则构建一个新的字典,将它们映射到目标树中匹配的height-1树的列表。
  • 重复,使用height-1字典构建高度为2的树字典。
  • 重复一遍,建立一个高度为3的树,你明白了。最终,你将到达模式树的根,然后就完成了。

这是在二次时间,这不是很好,但通常的谷歌搜索并没有揭示更好的算法(主要是因为二叉树/有序树变体淹没了搜索空间)。如果有人知道更好,我会很高兴听到它。使用凯利的定理来反对所有的子树,我认为这是天真的,但是有一些记忆可能也会归结为二次方。