检查A是否是二叉树B的一部分

时间:2013-08-19 21:02:04

标签: algorithm graph binary-tree complexity-theory graph-algorithm

假设我有二叉树A和B,我想知道A是否是B的“部分”。我不只是在讨论子树。我想知道的是,B是否具有A所做的所有节点和边缘。

我的想法是,因为树本质上是一个图形,我可以将这个问题视为子图同构问题(即检查A是否是B的子图)。但根据维基百科,这是一个NP完全问题。

http://en.wikipedia.org/wiki/Subgraph_isomorphism_problem

我知道您可以使用O(n)算法检查A是否是B的子树(例如,使用预序和顺序遍历将树展平为字符串并检查子字符串)。我试图稍微修改一下,看看我是否也可以测试“部件”,但无济于事。这就是我被困住的地方。

除了使用子图同构外,还有其他方法可以查看此问题吗?我认为必须有更快的方法,因为二叉树更受限制,图形版本更简单。

提前致谢!

编辑:我意识到,对于我的问题,即使是强力方法的最坏情况也只需要O(m * n),这是多项式的。所以我想这毕竟不是NP完全问题。然后我的下一个问题是,是否存在比O(m * n)更快的算法?

2 个答案:

答案 0 :(得分:2)

我会分两步来解决这个问题:

  1. A中找到B的根(DFS的BFS)
  2. 使用递归算法验证A中是否包含B(给出该起始节点),如下所示(我编写了相同的疯狂伪语言,因为您没有指定语言。我认为这应该是可以理解的,无论你的背景如何)。请注意,aA(最初是根)的节点,bB的节点(最初是步骤1中找到的节点)
  3. function checkTrees(node a, node b) returns boolean
        if a does not exist or b does not exist then
            // base of the recursion
            return false
        else if a is different from b then
            // compare the current nodes
            return false
        else
            // check the children of a
            boolean leftFound = true
            boolean rightFound = true
    
            if a.left exists then
                // try to match the left child of a with
                // every possible neighbor of b
                leftFound = checkTrees(a.left, b.left)
                           or checkTrees(a.left, b.right)
                           or checkTrees(a.left, b.parent)
    
            if a.right exists then
                // try to match the right child of a with
                // every possible neighbor of b
                leftFound = checkTrees(a.right, b.left)
                           or checkTrees(a.right, b.right)
                           or checkTrees(a.right, b.parent)
    
            return leftFound and rightFound
    

    关于运行时间:让mA中的节点数,nB中的节点数。第一步中的搜索需要O(n)次。第二步的运行时间取决于我做出的一个重要假设,但这可能是错误的:我假设A的每个节点最多等于 {{1}的一个节点}}。如果是这种情况,第二步的运行时间为B(因为您永远不能在错误的方向上搜索太远)。因此总运行时间为O(m)

    在写下我的假设时,我开始怀疑这是不是过于简化了你的情况......

答案 1 :(得分:0)

你可以自下而上地比较树木:

  • 对于树A中的每个叶子,标识树B中的相应节点。
  • 从刚刚匹配的节点开始对两棵树中的根进行并行遍历。 具体来说,移动到A中节点的父节点,然后移向B中的根节点,直到遇到B中的相应节点(继续)或A中的标记节点(见下文,如果发现B中的匹配,则继续,否则失败)或B的根(失败)
  • 标记在A中访问的所有节点。

如果你没有失败,你就会成功; - )。

算法的主要部分在O(e_B)中运行 - 在最坏的情况下,B中的所有边都被访问了恒定次数。叶节点匹配将在O(n_A * log n_B)中运行,如果有B个顶点被排序,O(n_A * log n_A + n_B * log n_B + n) = O(n_B * log n_B)(对每个节点集进行排序,此后立即扫描结果)否则。

编辑: 重新阅读你的问题,上面提到的第2步更容易,因为对于A,B中的匹配节点,他们的父母也必须匹配(否则边缘集之间会出现不匹配)。当然,对最坏情况的运行时间没有影响。