leetcode 951. 翻转等价二叉树- 递归算法,改写迭代失败

题目描述:

我们可以为二叉树 T 定义一个翻转操作,如下所示:选择任意节点,然后交换它的左子树和右子树。

只要经过一定次数的翻转操作后,能使 X 等于 Y,我们就称二叉树 X 翻转等价于二叉树 Y。

编写一个判断两个二叉树是否是翻转等价的函数。这些树由根节点 root1root2 给出。

示例:

输入:root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7]
输出:true
解释:We flipped at nodes with values 1, 3, and 5.

提示:

  1. 每棵树最多有 100 个节点。
  2. 每棵树中的每个值都是唯一的、在 [0, 99] 范围内的整数。

思路:

题目是指,有两颗二叉树,对其中一颗树进行操作,交换它的某些节点的左右子树(注意是子树不是孩子节点的val),可以使它变为另一棵树。这样的两棵树,称为等价二叉树。

观察一下可以发现,交换子树对父节点没有任何影响。如果两个树的某处父节点不同,那对它们的子节点进行操作于事无补。所以,我们应该从根节点往叶子节点来考察。

扫描二维码关注公众号,回复: 4424656 查看本文章

从上往下考察树,让人很容易想到递归。等价二叉树中存在递归的现象吗?如果root1和root2是等价的,那么它要满足两个条件:

1,root1->val == root2->val

2,假定我们对root2进行操作,使它与root1相同。那么需要考虑问题:要交换root2的左右子树吗?要么不交换,使得root1和root2相同;要么交换,使得它们相同。所以,满足下面两个条件之一即可:

   a) root1的左子树和root2的左子树等价,root1的右子树和root2的右子树等价

  b) root1的左子树和root2的右子树等价,root1的右子树和root2的左子树等价

这里很明显存在递归了。设置递归出口:两颗空树一定是等价的。由于从上往下比较,算法一定会到达叶子节点,继而抵达递归出口,然后返回结果。代码实现如下:

bool flipEquiv(TreeNode* root1, TreeNode* root2) {
        if((root1 || root2) == NULL) return true;//递归出口
        //1,根节点的val相同。否则一定不等价
        if((root1 && root2) == NULL) return false;//一定不等价
        if(root1->val != root2->val) return false;
        //2,两颗左子树等价并且两颗右子树等价,
        //或者,root1的左子树等价root2的右子树,root1的右子树等价root2的左子树
        return (flipEquiv(root1->left,root2->left)&&flipEquiv(root1->right,root2->right))    
       || (flipEquiv(root1->left,root2->right)&&(flipEquiv(root1->right,root2->left)));    
    }

在极端(返回值为 true) 的情况下,两棵树的所有节点一一比对,所以时间复杂度应该是O(n)。测试运行时间 4ms。

现在想想能不能对代码优化。

可以用条件语句把a) 和 b)明确分开吗?毕竟它们大部分情况必然有一个是false(同一个root左孩子的val和右孩子的val不等),此时只需要考虑val相等的一对子树。但是,把这两种情况完全分开的条件太繁琐了(存在这种情况:那一层次的四个孩子节点的val值可能全部相同),我不太想牺牲代码的简洁、易读去换取少量的性能提升。

可以增加递归出口吗?其实到了叶子节点这里,就可以直接判定是否等价了,无需再往下深入。代码稍作改动,增加递归出口:

    //当root1和root2同为叶子节点
    if(((root1->left || root1->right)||(root2->left||root2->right))==NULL)
        {
            if(root1->val == root2->val) return true;
            else return false;
        }

测试运行时间 4ms。

再来想一想,能不能把它改成循环。先假设我们有一个栈,可以用于保存节点。里面初值是root1和root2,每次出栈至少出一对。对于任意两个结点的四个孩子,要判断4对孩子节点是否等价...这四个值可真可假。对它们进行组合、判断,如果条件a和条件b都不满足,就可以返回false了。而问题在于:条件a与条件b是和后面所有节点相关的,如果四个孩子节点的val值都相同,根本没有办法在遍历到某对节点时,就说它们等价或者不等价,那么条件a和条件b都有可能成立,也都有可能不成立。这样的话,这4对孩子节点,都要入栈。那么出栈的时候,哪些节点对应条件a,哪些节点对应条件b?在这一层可以判断清楚吗?假如这一层所有孩子节点val又相同,又要放到下一层去判断。在一次循环中要判断的条件会动态变化,越来越复杂,而不是固定的。我当前的能力,还做不到这样。

那先就这样吧,以后再试。

猜你喜欢

转载自blog.csdn.net/liusiweix/article/details/84888528