101.对称二叉树 | 递归 + 迭代

本文介绍了如何解决LeetCode上的对称二叉树问题,通过递归和迭代两种方法对比节点,重点在于内侧和外侧节点的对比。递归法通过判断左右节点的空值情况及值的相等性,再递归比较子树。迭代法则利用队列按对存储节点,逐层检查对称性。

对称二叉树

leetcode : https://leetcode.cn/problems/symmetric-tree/

参考 对称二叉树

递归思路

首先在开始时, 一定要注意, 对称二叉树对比的并不是一个节点的左右子树, 而是两棵树, 这个很关键!

image-20230111211254708

对比时是内侧和内侧对比, 外侧和外侧对比,

递归三步 :

  1. 确定递归的参数以及返回值

    本题中需要去对比内侧和外侧节点是否对称, 所以返回值是 boolean 类型

     private boolean compare(TreeNode left, TreeNode right)
    
  2. 确定终止条件 :

    • 我们在对比两棵树是否对称时, 主要分为以下几种情况

    • 左节点为空, 右节点不为空, 不对称 (注意这里是左节点和右节点, 而不是左子树和右子树)

    • 左节点不为空, 右节点为空, 不对称

    • 左右节点都为空, 对称

    • 左右节点都不为空, 此时要比较二者的值

image-20230111211746071
 		// 终止条件 : 避免操作空指针
        // 1. 左节点为空, 右节点不为空 不对称
        if(left == null && right != null) {
            return false;
        }
        // 2. 左节点不为空, 右节点为空 不对称
        if(left != null && right == null) {
            return false;
        }
        // 3. 左节点为空, 右节点为空 对称
        if(left == null && right == null) {
            return true;
        }
  1. 确定当前层逻辑
  • 在确定了终止条件为空的情况下, 当前层的逻辑是 判断两个节点值是否相同
  • 然后分别对比两棵树的内侧和外侧的节点
  		if(left.val != right.val) {
            return false;
        }
        // 1. 对比内侧
        boolean inside = compare(left.right, right.left);
        // 2. 对比外侧
        boolean outside = compare(left.left, right.right);

        return inside && outside;

递归流程

初始状态 : 传入 left 和 right

image-20230111213511399

首先是会判断是否存在 left 或者 right 为空的情况(终止条件) , 这个也是为了防止处理空节点

然后我们要对比以 left 为根节点 和 以right 为根节点的树, 如下图所示, 这个很关键

image-20230111213710704

首先对比内侧的 :

boolean inside = compare(left.right, right.left);

image-20230111213848145

同理, 我们对比内侧其实就是对比的 left.right 和 right.left 这两棵子树是否对称

我们假设, 当前的树不再向下延伸(这棵树就这么大), 此时的逻辑是

  1. 先判断是否满足终止条件, 也就是存在 left.right 或者 right.left 为空的情况, 此时不对称, 直接就返回结果了

    boolean inside = compare(left.right, right.left);
    

    inside = false , 直接结束

  2. 假设没有到达终止条件, 先比较 left.right.val 和 right.left.val 是否相等

  3. 比较内侧和外侧的节点是否对称

  4. 最后会返回 left.right 和 right.left 这两棵树的是否对称的结果

需要补充的是, 当最后到达叶子节点时, 其实叶子节点也可以看做左右子节点都为空的树

image-20230111214540654

如上图所示, 此时他们会被下面的代码处理

// 3. 左节点为空, 右节点为空 对称
        if(left == null && right == null) {
            return true;
        }

递归代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {
        
        // 终止条件 : 避免操作空指针
        // 1. 左节点为空, 右节点不为空 不对称
        if(left == null && right != null) {
            return false;
        }
        // 2. 左节点不为空, 右节点为空 不对称
        if(left != null && right == null) {
            return false;
        }
        // 3. 左节点为空, 右节点为空 对称
        if(left == null && right == null) {
            return true;
        }

        // 当前层的处理逻辑 : 左右子树都不为空
        if(left.val != right.val) {
            return false;
        }

        // 下一层
        // 1. 对比内侧
        boolean inside = compare(left.right, right.left);
        // 2. 对比外侧
        boolean outside = compare(left.left, right.right);

        return inside && outside;
    }

}

迭代思路

迭代思路和递归思路有一些相似, 都是外侧和外侧对比, 内侧和内侧对比 !

核心的点就是把每一层的取出来, 然后按照 内侧对内侧 和 外侧对外侧的顺序存入队列

这样的话, 每次取出栈顶的两个都是一对的

image-20230111223028579

如上图, 核心点是存入队列的时候, 存入的顺序是成对的!

因为取出来正好的成对的, 这样取出节点后, 我们只需要判断是否对称即可

  1. 左右节点都为空, 不处理, 直接跳过 (对称)
  2. 左节点为空, 右节点不为空 (不对称)
  3. 左节点不为空, 右节点为空 (不对称)
  4. 左右节点都不为空, 比较两个节点的值是否相同

迭代代码

需要注意的是 , LinkedList 是可以存储null值的, 其他的数据结构不一定支持

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();

        queue.add(root.left);
        queue.add(root.right);

        while(!queue.isEmpty()) {

            TreeNode left = queue.poll();
            TreeNode right = queue.poll();

            // 判断
            // 1. 左空, 右不空, 不对称
            if(left == null && right != null ) {
                return false;
            }
            // 2. 左不为空, 右为空 不对称
            if (left != null && right == null) {
                return false;
            }
            // 左右都为空, 就不处理
            if(left == null && right == null) {
                continue;
            }

            // 左右都不为空的情况, 比较二者的值
            if(left.val != right.val) {
                return false;
            }

            // 将下一层子节点添加到队列
            // 注意, 要按照对称的顺序, 外侧对外侧, 内侧对内侧
            // left.left 和 right.right
            // left.right 和 right.left
            // 外侧
            queue.add(left.left);
            queue.add(right.right);
            // 内侧
            queue.add(left.right);
            queue.add(right.left);
        }

        return true;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兀坐晴窗独饮茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值