在二叉树相关算法题中,对称性判断是一道经典且高频的题目。它不仅考察对树结构的理解,更考验递归与迭代两种核心编程思想的灵活运用。本文将深入剖析 LeetCode 101 题,带你从思路推导到代码落地,彻底吃透对称二叉树问题。无论你是准备面试,还是夯实基础,这篇文章都值得一看。

为什么对称二叉树值得深入思考?

对称二叉树,简单来说就是一棵树关于根节点左右镜像对称。判断对称性,不能只比较左右子节点的值是否相等,因为对称要求的是左子树的左子树与右子树的右子树相等,左子树的右子树与右子树的左子树相等。这种嵌套的对应关系,恰好是递归最擅长的场景。

同时,它也是理解迭代遍历、队列应用的好素材。许多算法题(如镜像树、翻转树)都与其有异曲同工之妙。掌握本题的两种解法,能帮你构建更系统的树形问题解决框架。

一、题目回顾与初步思路

题目要求判断给定的一棵二叉树是否对称。示例中,对称树返回 true,非对称树返回 false。题目保证了至少有一个节点,因此无需处理空树。

拿到题目,常见的初步想法有三种:

  • 层序遍历思路:逐层检查每层节点是否对称。但实现稍显繁琐,且需要额外记录空位。
  • 翻转比较法:先将左子树翻转成右子树的镜像,再与右子树逐节点比较。思路直观,但效率一般。
  • 递归比较法:直接递归比较对称位置的节点。这是最优雅的方案,也是本文重点。

如果已经做过“翻转二叉树”这道题,你可能会想:把左子树翻转后,和右子树比较不就行了?理论上可行,但递归解法更符合树的天然结构,也更高效。下面我们详细展开递归解法。

二、递归解法:三步走,思路清晰

递归的难点在于如何设计递归函数。这里推荐按照递归三要素来思考:

1. 确定参数和返回值

对于对称判断,我们不需要额外的数组或计数器,只需要比较两个节点是否对称。因此,递归函数的参数应该是两个节点(左子树的当前节点和右子树的当前节点),返回值是布尔类型 true 或 false。

2. 确定终止条件

终止条件是递归的边界,这里需要分情况讨论:

  • ✅ 当两个节点都为空(即越过叶子),说明对称,返回 true。
  • ⚠️ 当其中一个为空,另一个非空,说明不对称,返回 false。
  • ❌ 当两个节点值不相等,说明不对称,返回 false。

以上三种情况涵盖了所有终止可能。注意,题目保证至少有一个节点,所以不需要考虑根节点为空的情况。

3. 确定单层递归逻辑

如果当前两个节点都不为空且值相等,那么我们需要继续比较它们的子树。根据对称的定义:

  • 比较左节点的左子树右节点的右子树
  • 比较左节点的右子树右节点的左子树

如果这两个递归调用都返回 true,则整体对称。这个逻辑可以用以下伪代码辅助理解:

function isSymmetric(left, right):
    if left == null && right == null: return true
    if left == null || right == null: return false
    if left.val != right.val: return false
    return isSymmetric(left.left, right.right) && isSymmetric(left.right, right.left)

这种设计巧妙地将复杂问题分解为对称子问题,是递归思想的经典应用。

代码实现(以 JavaScript 为例)

下面是递归解法的代码,注释清晰,可直接运行。

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode() : val(0), left(nullptr), right(nullptr) {}
*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
return check(root->left,root->right);
}
bool check(TreeNode* p,TreeNode* q){
// 两个都跨过叶子节点指向null
if (!p&&!q){
return true;
}
// 不是上面的情况,说明至少有一个非空,此时存在一个空/两个节点的值不相等说明不对称
if (!p||!q||p->val!=q->val){
return false;
}
//要求 L.left节点的值等于R.right节点的值 且 L.right节点的值等于R.left节点的值 才对称
return check(p->left,q->right)&&check(p->right,q->left);
}
};

以上代码的时间复杂度为 O(n),因为每个节点恰好被访问一次。空间复杂度为 O(n),取决于递归调用栈的深度,最坏情况下树退化为链表,递归深度为树的高度。

三、迭代解法:队列实现,另一种视角

除了递归,我们还可以用迭代法解决。核心思路是使用一个队列,每次取出两个节点进行比较,然后将它们的对称子节点成对入队,继续下一轮比较。

具体步骤如下:

  1. 将根节点的左孩子和右孩子加入队列。
  2. 每次从队列中取出两个节点,比较它们的值。
  3. 将左节点的左孩子与右节点的右孩子放入队列。
  4. 将左节点的右孩子与右节点的左孩子放入队列。
  5. 循环直到队列为空,或发现不对称返回 false。

这种方法避免了递归可能出现的栈溢出问题,在树的深度较大时更稳定。以下是 Java 语言的实现:

class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null || (root.left==null && root.right==null)) {
return true;
}
//用队列保存节点
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
  //将根节点的左右孩子放到队列中
  queue.add(root.left);
  queue.add(root.right);
  while(queue.size()>0) {
  //从队列中取出两个节点,再比较这两个节点
  TreeNode left = queue.removeFirst();
  TreeNode right = queue.removeFirst();
  //如果两个节点都为空就继续循环,两者有一个为空就返回false
  if(left==null && right==null) {
  continue;
  }
  if(left==null || right==null) {
  return false;
  }
  if(left.val!=right.val) {
  return false;
  }
  //将左节点的左孩子, 右节点的右孩子放入队列
  queue.add(left.left);
  queue.add(right.right);
  //将左节点的右孩子,右节点的左孩子放入队列
  queue.add(left.right);
  queue.add(right.left);
  }
  return true;
  }
  }

迭代解法的时间复杂度同样为 O(n),空间复杂度为 O(n),因为队列中最多存储两层的节点。

在实际面试中,建议先给出递归解法,再补充迭代解法,展示你对问题多角度的理解。

四、常见错误与避坑指南

在实现过程中,不少初学者容易犯以下错误:

  • 只比较左右孩子:这是最典型的错误。对称判断必须深入子树,而非仅仅比较根节点的左右孩子。
  • 忽略空节点处理:在递归或迭代中,空指针的判断顺序至关重要,否则会导致空指针异常。
  • 递归参数设计错误:有些同学会把递归函数设计为只接收一个节点,导致无法同时比较左右子树。

建议在写代码前,先在纸上画出树的结构,模拟递归调用的过程,能有效避免这些错误。

五、延伸思考与多语言实践

对称二叉树的思想可以迁移到其他问题,例如判断两棵树是否相同、求二叉树的最小深度等。此外,不同编程语言的实现也各有特色:

  • C++:可以借助指针和引用,代码简洁高效。
  • TypeScript:类型定义能增强代码可读性,适合大型项目。
  • Go:结构体和方法的设计让树操作更直观。

如果你正在学习算法,建议尝试用至少两种语言实现本题,加深理解。下面简单展示 Go 语言的递归写法思路,其他语言可自行练习。

(此处可插入其他语言代码示例)

练习时,可以配合 LeetCode 的在线调试功能,逐步查看递归调用过程,会更有帮助。

总结一下,对称二叉树的核心在于理解对称的递归定义,并熟练掌握递归与迭代两种实现方式。建议先手写递归,再尝试用队列模拟迭代,最后对比两种方法的性能差异。希望这篇文章能帮你彻底掌握这道经典题目,举一反三,面试不慌!

如果你觉得本文有用,欢迎分享给更多学习算法的朋友,一起进步!

[AFFILIATE_SLOT_1] [AFFILIATE_SLOT_2]