【问题标题】:How to check if a tree is a subtree of another tree?如何检查一棵树是否是另一棵树的子树?
【发布时间】:2013-07-22 04:26:43
【问题描述】:

所以,我有明显的蛮力算法,如下所示

int isSubtree (binTree *S, binTree *T)
{
    if (S == NULL)
        return 0;
    return (isEqual (S,T) || isSubtree (S->left, T) || isSubtree (S->right, T));
}

int isEqual (binTree *S, bintree *T)
{
    if (S==NULL && T==NULL)
        return 1;
    if (S==NULL || T==NULL)
        return 0;
    if (S->val == T->val)
        return isEqual(S->left,T->left) && isEqual (S->right,T->right);
    else
        return 0;
}

但这是 O(n²) 的方法。

我有另一种方法,如下所示,是 O(n) 我们以中序方式遍历第一棵树并将其存储在一个数组中。然后我们遍历第二棵树并以有序的方式存储它。现在,如果第二个数组是第一个数组的子数组,我们继续并重复相同的过程来进行前序遍历。如果两个查询的结果都是 TRUE,则树是第一棵树的子树。否则,不。

谁能告诉我以下算法是否可行?

对于这个问题是否有更优化空间的解决方案?

注意:我需要两个数组,因为我存储了两个数组的遍历,有没有我可以只使用一个数组?就像我会存储其中一棵树的中序遍历,然后在遍历另一棵树时使用该数组检查子数组条件。 或者可能没有额外的空间但 O(n) 时间复杂度?

注意:子数组是指元素应该连续出现,即

{2,3,5} is a subarray of {1,2,3,5} but not a subarray of {1,2,3,4,5}

【问题讨论】:

  • 您的蛮力算法的复杂度仅为 O(n)。你怎么说它是n^2个soln?
  • 在每次递归中,您实际上是在删除一个节点。所以,它只是 O(n),这需要做 3 次。
  • @Karthikeyan:假设S 中的所有节点都持有1,而T 中的所有节点都是1,除了最右边的一个孩子持有2。那么每个isEqual 检查将遍历所有节点T,最后才发现不匹配。对S 中的每个节点重复此操作。
  • 知道了,对不起,我的错..
  • isEqual() 函数是 O(n),在最坏的情况下,我们遍历 isSubtree() 中的每个节点,我们会得到 O(n²) 的整体时间复杂度,不是吗?

标签: c++ c tree


【解决方案1】:

总结:考虑在每个节点中存储哈希和/或子树大小以加快搜索速度。你提出的算法被破坏了。

你提出的算法 - 坏了?

如果我正确理解了您提出的替代算法,那么它就不起作用。作为一个反例,请考虑:

  T          S
  x          x
 / \        / \
y   z      y   z
                \
                 q

T 有中序遍历 yxz,前序 xyz。 S有中序遍历yxzq,前序xyzq。

因此,尽管 T 不是有效匹配项(根据您的递归方法),但 T 的遍历被发现嵌入在 S 中。

在递归匹配过程中快速消除子树

我一直在按照 Karthikeyan 的建议进行思考 - 在每个节点处存储子树深度,因为它可以让您消除大量比较。当然,如果动态维护,它也会使某些树操作更加昂贵 - 必须优先考虑这些操作或子树查找期间的额外命中。

存储子树元素的散列是另一种可能性。什么是有意义的取决于与子树相比,树的结构和数据更新的动态程度,以及从整体性能的角度来看,两者是否更重要。

进一步阅读

无论如何,有很多关于这个的现有问题,例如。 Find whether a tree is a subtree of other。哦——也发现了这个——Determine if a binary tree is subtree of another binary tree using pre-order and in-order strings——这似乎支持我上面的逻辑,因为你说递归方法是正确的但很慢。

【讨论】:

  • 在 cmets 中的用户指针。该算法实际上是不正确的。我正在研究 Karthikeyan 的解决方案并试图提出算法。
【解决方案2】:

如何进行深度优先搜索并存储每个节点的子树节点数,并仅比较其节点数与所讨论的另一棵树相等的父节点的子树。

【讨论】:

  • 想象两棵树都是完全二叉树,每个节点(叶子除外)都有两个子节点。
  • 啊,等等,我收回反对票。事实上,我想我误解了原来的问题。看起来只有当叶子与叶子对齐时 T 才被视为 S 的子树(因此,具有单个元素的树恰好与 S 中的根匹配的树实际上不被视为 S 的子树)。
  • 我不确定我是否关注你。你能用代码或者只是一个粗略的算法来支持你的方法吗?
  • 简单,有一个包含每个节点指针的字典以及该节点的子树计数。并检查其计数等于必须进行比较的子树的子树。做一个树等价检查哪个 O(n) 来确认它们是否相等。
【解决方案3】:

我们可以使用中序遍历和 DFS(在二叉树中,它简化为前序遍历)。现在首先使用 DFS,修改两棵树的数据结构,并在每个节点存储其下的子树的数量。 之后为两棵树编写中序遍历,然后将字符串与 KMP 匹配。在 O(n+m)(两棵树中的 n 和 m- 个节点)中,我们可以找出不同的匹配。我们可以使用散列连接到带有 DFS 的修改后的图。在每次与 KMP 匹配时,我们可以比较两者的 DFS 修改图(在子树的数量上),如果它与整个序列也匹配,则它是子树,否则我们去 KMP 的另一个匹配等等开。

在上面的例子中,修改后的数据结构fot 'T'在DFS之后是[x(2);y(0);z(0)] & for 'S' [x(3);y(0) ;z(1);q(0)]。 'T'的顺序:yxz 'S'的顺序:yxzq

我们得到匹配“yxz”。现在我们进入 DFS 修改后的结构。 x 处不匹配;所以,“T”不是“S”的子树。

【讨论】:

    【解决方案4】:

    在数组{2,3,5}中,根可以是2、3、5,所以不能用这样的数组来表示树。

    如果 A 是 B 的子树(类似于您的代码),并假设 leafs(x) 是从左到右“树 x 的叶节点”的数组,那么叶 (A) 是叶 (B) 的子串。

    一旦你找到了上面的子字符串,检查从叶子到根的节点,以确保它真的是一个子树。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多