LeetCode 101. 对称二叉树

题目描述

101. 对称二叉树

image-20230305212333535

image-20230305212330371

思路分析

解法一:递归(推荐)

核心思路

对称的本质是根节点的左子树与右子树互为镜像——即「外侧一对」和「内侧一对」同时对称。

定义辅助函数 isMirror(p, q),判断以 p 和 q 为根的两棵子树是否互为镜像:

  1. p == nil && q == niltrue(两者同时为空,对称)
  2. p == nil || q == nilfalse(一个为空另一个不为空,不对称)
  3. p.val != q.valfalse(值不同,不对称)
  4. 递归:isMirror(p.left, q.right) && isMirror(p.right, q.left)
    • 外侧对:p 的左子树 vs q 的右子树
    • 内侧对:p 的右子树 vs q 的左子树

与 100 题(相同的树)的对比:100 题是同侧比较,101 题是交叉比较。


复杂度分析

  • 时间复杂度:O(n),其中 n 为树的节点总数,每个节点恰好访问一次
  • 空间复杂度:O(h),其中 h 为树的高度,递归调用栈深度;最坏情况(链式树)退化为 O(n)
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isMirror(root.left, root.right);
    }

    // 判断两棵子树是否互为镜像
    private boolean isMirror(TreeNode p, TreeNode q) {
        // 两者同时为空,对称
        if (p == null && q == null) {
            return true;
        }
        // 一个为空另一个不为空,不对称
        if (p == null || q == null) {
            return false;
        }
        // 当前节点值不同,不对称
        if (p.val != q.val) {
            return false;
        }
        // 交叉递归:外侧对 + 内侧对同时满足才对称
        return isMirror(p.left, q.right) && isMirror(p.right, q.left);
    }
}
func isSymmetric(root *TreeNode) bool {
    if root == nil {
        return true
    }
    // 判断两棵子树是否互为镜像
    var isMirror func(p, q *TreeNode) bool
    isMirror = func(p, q *TreeNode) bool {
        // 两者同时为空,对称
        if p == nil && q == nil {
            return true
        }
        // 一个为空另一个不为空,不对称
        if p == nil || q == nil {
            return false
        }
        // 当前节点值不同,不对称
        if p.Val != q.Val {
            return false
        }
        // 交叉递归:外侧对 + 内侧对同时满足才对称
        return isMirror(p.Left, q.Right) && isMirror(p.Right, q.Left)
    }
    return isMirror(root.Left, root.Right)
}

解法二:迭代(BFS 成对入队)

核心思路

用队列模拟递归的过程,将需要比较的节点成对入队:

  1. 初始将 root.leftroot.right 成对入队
  2. 每次从队列中取出两个节点 t1、t2 进行比较
  3. 若值相等,按镜像顺序将 (t1.left, t2.right)(t1.right, t2.left) 成对入队
  4. 任意时刻值不相等则返回 false


复杂度分析

  • 时间复杂度:O(n),其中 n 为树的节点总数,每个节点恰好访问一次
  • 空间复杂度:O(n),其中 n 为树的节点总数,队列最多同时存储 n 个节点
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 成对入队,每次取出两个节点进行镜像比较
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root.left);
        queue.offer(root.right);

        while (!queue.isEmpty()) {
            TreeNode t1 = queue.poll();
            TreeNode t2 = queue.poll();

            // 两者同时为空,继续
            if (t1 == null && t2 == null) {
                continue;
            }
            // 一个为空或值不同,不对称
            if (t1 == null || t2 == null || t1.val != t2.val) {
                return false;
            }
            // 按镜像顺序将子节点成对入队
            queue.offer(t1.left);
            queue.offer(t2.right);
            queue.offer(t1.right);
            queue.offer(t2.left);
        }

        return true;
    }
}
func isSymmetric(root *TreeNode) bool {
    if root == nil {
        return true
    }

    // 成对入队,每次取出两个节点进行镜像比较
    queue := []*TreeNode{root.Left, root.Right}

    for len(queue) > 0 {
        t1 := queue[0]
        t2 := queue[1]
        queue = queue[2:]

        // 两者同时为空,继续
        if t1 == nil && t2 == nil {
            continue
        }
        // 一个为空或值不同,不对称
        if t1 == nil || t2 == nil || t1.Val != t2.Val {
            return false
        }
        // 按镜像顺序将子节点成对入队
        queue = append(queue, t1.Left, t2.Right, t1.Right, t2.Left)
    }

    return true
}

相似题目

题目 难度 考察点
100. 相同的树 简单 递归、DFS
104. 二叉树的最大深度 简单 递归、DFS
226. 翻转二叉树 简单 递归
572. 另一棵树的子树 中等 递归、DFS
110. 平衡二叉树 简单 递归、DFS
111. 二叉树的最小深度 简单 BFS、DFS
617. 合并二叉树 简单 递归
112. 路径总和 简单 DFS
543. 二叉树的直径 简单 DFS
222. 完全二叉树的节点个数 中等 递归
本文作者:
本文链接: https://hgnulb.github.io/blog/2026/52259577
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!