确定两个二叉树是否相等

时间:2009-09-27 05:00:59

标签: algorithm binary-tree

如果两个给定的二叉树是相等的 - 在结构和内容中找到什么是有效的算法?

7 个答案:

答案 0 :(得分:24)

这是一个小问题,但我会按照以下方式调整早期解决方案......

eq(t1, t2) =
  t1.data=t2.data && eq(t1.left, t2.left) && eq(t1.right, t2.right)

原因是不匹配可能很常见,最好在提前再次检测之前检测(并停止比较)。当然,我假设是短路&&运营商。

我还要指出,这正在掩盖一些问题,正确处理结构上不同的树,并结束递归。基本上,需要对t1.left等进行一些空检查。如果一棵树有一个null .left但另一棵没有,你发现了结构上的差异。如果两者都有null .left,则没有区别,但是你已经达到了一个叶子 - 不要进一步递减。只有当两个.left值都为非null时,才会递归检查子树。当然,这同样适用于.right。

你可以包括支票,例如(t1.left == t2.left),但这只有在子树可以为两棵树物理共享(相同的数据结构节点)时才有意义。这种检查将是避免在不必要的地方进行递归的另一种方法 - 如果t1.left和t2.left是相同的物理节点,那么您已经知道那些整个子树是相同的。

C实现可能是......

bool tree_compare (const node* t1, const node* t2)
{
  // Same node check - also handles both NULL case
  if (t1 == t2)  return true;

  // Gone past leaf on one side check
  if ((t1 == NULL) || (t2 == NULL))  return false;

  // Do data checks and recursion of tree
  return ((t1->data == t2->data) && tree_compare (t1->left,  t2->left )
                                 && tree_compare (t1->right, t2->right));
}

编辑回复评论......

使用它进行完整树比较的运行时间最简单地表示为O(n),其中n有点像树的大小。如果你愿意接受一个更复杂的界限,你可以得到一个更小的界限,如O(最小值(n1,n2)),其中n1和n2是树的大小。

解释基本上是递归调用仅对左树中的每个节点进行一次(最多)一次,并且仅对右树中的每个节点进行一次(最多)一次。由于函数本身(不包括递归)最多只能指定一定量的工作(没有循环),所以包括所有递归调用的工作只能与较小树的大小相同,而不是常量。

你可以进一步分析以使用树的交叉点来获得更复杂但更小的界限,但是大O只给出上限 - 不一定是最低可能的上界。除非你试图用它作为一个组件构建一个更大的算法/数据结构,否则可能不值得进行这种分析,因此你知道某些属性将永远应用于那些可能允许你更紧密绑定的树。更大的算法。

形成更严格界限的一种方法是考虑两棵树中节点的路径集。每个步骤是L(左子树)或R(右子树)。因此,根用空路径指定。根的左孩子的右孩子是“LR”。定义一个函数“paths(T)”(数学上 - 不是程序的一部分)来表示树中的有效路径集 - 每个节点一条路径。

所以我们可能会......

paths(t1) = { "", "L", "LR", "R", "RL" }
paths(t2) = { "", "L", "LL", "R", "RR" }

相同的路径规范适用于两个树。并且每个递归始终遵循两个树的相同左/右链接。因此,递归访问这些集合的路径中的路径,并且我们可以使用此指定的最严格的边界是该交集的基数(仍然与每次递归调用的工作的常量限制)。

对于上面的树结构,我们对以下路径进行递归...

paths(t1) intersection paths(t2) = { "", "L", "R" }

因此,我们在这种情况下的工作最多限制为tree_compare函数中非递归工作的最大成本的三倍。

这通常是不必要的细节量,但显然路径集的交集最多与最小原始树中的节点数一样大。无论O(n)中的n是指一个原始树中的节点数还是两个中节点的总和,这显然不小于最小值或交点。因此O(n)并不是一个如此紧密的界限,但它仍然是一个有效的上界,即使我们有点模糊我们正在讨论的大小。

答案 1 :(得分:4)

模块堆栈溢出,类似

eq(t1, t2) =
    eq(t1.left, t2.left) && t1.data=t2.data && eq(t1.right, t2.right)

(这推广到所有树形结构代数数据类型的等式谓词 - 对于任何结构化数据,检查其每个子部分是否等于另一个子部分中的每一个。)

答案 2 :(得分:3)

我们也可以进行两次遍历中的任何一种(预订,后序或有序),然后比较两棵树的结果。如果它们相同,我们可以确定它们的等价性。

答案 3 :(得分:1)

您可能要完成的更为通用的术语是graph isomorphism。在该页面上有一些算法可以做到这一点。

答案 4 :(得分:1)

因为事实证明 - 只要我们有以下内容,就可以重新创建二叉树:

  1. 有序遍历中遇到的节点序列。
  2. 预订或后序遍历中遇到的节点序列
  3. 如果两个二叉树具有相同的有序和[预订或后序]序列,则它们在结构和值方面应该相等。

    每次遍历都是O(n)操作。遍历完成4次,并且比较来自相同类型的遍历的结果。 O(n)* 4 + 2 =>上) 因此,时间复杂度的总顺序将是O(n)

答案 5 :(得分:0)

我会写如下。以下代码适用于大多数函数语言,如果您的数据类型是可清除的(例如,不是字典或列表),甚至可以在python中使用:

  • 拓扑平等(结构相同,即Tree(1,Tree(2,3))==Tree(Tree(2,3),1)):

    tree1==tree2表示set(tree1.children)==set(tree2.children)

  • 订购平等:

    tree1==tree2表示tree1.children==tree2.children

(Tree.children是一个有序的儿童名单)

您不需要处理基本情况(叶子),因为已经为它们定义了相等性。

答案 6 :(得分:0)

bool identical(node* root1,node* root2){
    if(root1 == NULL && root2 == NULL)
        return true;
    if(root1==NULL && root2!=NULL || root1!=NULL && root2 == NULL)
        return false;
    if(root1->data == root2->data){
        bool lIdetical = identical(root1->left,root2->left);
        if(!lIdentical)
            return false;
        bool rIdentical = identical(root1->right,root2->identical);
        return lIdentical && rIdentical;
    }
    else{
        printf("data1:%d vs data2:%d",root1->data,root2->data);
        return false;
    }
}

我不知道这是否最有效,但我认为这可行。