确定二叉树是否对称的思考过程

时间:2017-10-13 05:05:49

标签: algorithm recursion tree

以下是问题描述:

给定二叉树,检查它是否是自身的镜像(即,围绕其中心对称)。

例如,这个二叉树[1,2,2,3,4,4,3]是对称的:

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是以下[1,2,2,null,3,null,3]不是:

    1
   / \
  2   2
   \   \
   3    3

来自:Determine if tree is symmetric

我花了很多时间来解决问题,我想出的解决方案是进行水平顺序遍历并检查每个级别的值是否为回文。这个实现通过了leetcode的测试。然而,当我阅读社论时,我看到了一个极短的递归程序,我一直无法理解它。

public boolean isSymmetric(TreeNode root) {
    return isMirror(root, root); }

public boolean isMirror(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return true;
    if (t1 == null || t2 == null) return false;
    return (t1.val == t2.val)
        && isMirror(t1.right, t2.left)
        && isMirror(t1.left, t2.right);}
  1. 如何证明上述递归版本的正确性? (我想这可以归纳地证明了吗?)
  2. 有人可以在提出这样的解决方案时概述思考过程。您是通过实际可视化调用堆栈来验证解决方案还是有一个良好的高级思维框架来推理这些问题?
  3. 我理解树本身就是一个递归数据结构,即由左右子树组成,遵循相同的结构,但出于某种原因,当我尝试验证此解决方案的有效性时,我尝试可视化递归调用,最终我的思绪变得纠结。 This guy已经很好地解释了调用堆栈如何在递归过程中展开,但我只是想改进我的思维过程,这样简单的" easy"递归问题,因此我发布在这里。

    (FWIW,我熟悉递归/ DFS /回溯以及呼叫流程如何,但我仍然陷入困境并验证上述问题的高级递归思想)

    感谢您提供帮助。

2 个答案:

答案 0 :(得分:0)

这是可以使用光滑递归算法完成的解决方案之一。这个想法是保持两个引用最初指向根,然后将一个子树移动到左边,将另一个子树移动到相反的方向,反过来两个子节点的遍历方向然后指向对称节点子树(如果存在)

此处t1t2指的是左侧和右侧子树。

 if (t1 == null || t2 == null) return false;

这一步做的是检查是否存在左右子树,因为如果我们没有任何一个子树,那么它就不能对称,所以我们返回false

 if (t1 == null && t2 == null) return true;

这解释了叶节点可以将null作为左右子树。所以我们回归真实;

return (t1.val == t2.val)
        && isMirror(t1.right, t2.left)
        && isMirror(t1.left, t2.right);}

可以重写为

 if(t1.val != t2.val) return false;
 auto left =  isMirror(t1.right, t2.left)
 auto right = isMirror(t1.left, t2.right);
 return left && right 

现在我们知道子树都是有效的(即非空),然后检查它们的值以检查它们是否相同。如果不相同,我们可以返回false,因为没有必要进一步观察。

我们可以比较的原因是因为我们知道它必须是一个完全的对称树,我们可以将左子树(t1)移动到左边和右边的子树(t2)向右移动右子树上的对称节点。

                        1 (t1, t2)
                      /  \
                     2    2
                    / \  / \
                   4  5  5  4

isMirror(t1.right, t2.left)之后

                        1 
                      /  \
                (t2) 2    2(t1)
                    / \  / \
                   4  5  5  4

再次递归调用isMirror(t1.right, t2.left)

                        1 
                      /  \
                     2    2
                    / \  / \
              (t2) 4  5  5  4(t1)

现在,这将调用它的子节点只返回true,因为它们都是null。然后检查t1t2的值,并返回truefalse。然后现在调用isMirror(t1.left, t2.right)来到达此处。

                       1 
                     /   \
                    2     2
                   / \    / \
                  4  5   5  4
                    (t2)(t1)

现在与上面的步骤相同并展开调用堆栈。

因此,在堆栈帧处,我们left指示t1的左子树是否与t2的右子树对称,而right是否相反。< / p>

由于在递归检查孩子之前我们已经检查了t1.val是否等于t2.val,我们知道根是相等的,如果它的孩子是平等的,那么我们返回return left && right t1的子树与t2的子树对称

如果这有点令人费解,你可以在papare上找出它并检查它可能会让事情变得更好。

希望这会有所帮助。

答案 1 :(得分:0)

  

如何证明上述递归版本的正确性? (一世   猜猜这可以归纳证明吗?)

是的,你可以通过归纳来证明。

  

有人可以勾勒出想出这样一个想法的过程   解。您是否通过实际可视化呼叫来验证解决方案   堆栈还是有一个很好的高层次思考框架来推理   这样的问题?

我用两种方式解决了问题 - 级别顺序遍历和递归。通过看到这个问题,我的第一个想法是使用级别顺序遍历。在第一次尝试时,思考次优解(带有额外空间)并不羞耻。然后我想出了递归方法。

我认为这都是关于练习的。您解决的递归问题越多,您开始在头部思考/可视化的对应递归树就越多。在开始时,我努力使用它并使用调试器的帮助来查看每次调用中函数堆栈的内容。但是调试非常耗时,而且您无法在白板编码中找到调试范围。现在在我的水平上,我可以通过在笔和模拟中模拟,通过在头部和硬脑中看到它来找出简单/中等递归问题。纸/白板。

这些东西会随着经验和更多练习而改善。某些数据结构问题的经验 - 看到并解决了大量二叉树(指针表示)/ BST问题的人比那些能够处理非常好的递归但没有解决二叉树问题的人更有可能胜过这里。得多。

希望这有帮助!