LeetCode第101题:对称二叉树
题目描述
给你一个二叉树的根节点 root ,检查它是否轴对称。
难度
简单
问题链接
示例
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false
提示
- 树中节点数目在范围
[1, 1000]内 -100 <= Node.val <= 100
解题思路
对称二叉树是指二叉树的左右子树呈镜像对称。要判断一棵二叉树是否是对称的,我们可以采用以下两种方法:
方法一:递归
我们可以通过递归的方式来判断两个子树是否对称。具体来说,两个子树对称需要满足以下条件:
- 两个子树的根节点值相等。
- 第一个子树的左子树和第二个子树的右子树对称。
- 第一个子树的右子树和第二个子树的左子树对称。
我们可以定义一个辅助函数 isMirror(left, right) 来判断两个子树是否对称。
方法二:迭代(使用队列或栈)
我们也可以使用迭代的方式来判断两个子树是否对称。具体来说,我们可以使用队列或栈来存储需要比较的节点对。每次从队列或栈中取出两个节点进行比较,然后将它们的子节点按照对称的顺序放入队列或栈中。
算法步骤分析
递归方法:
- 定义一个辅助函数
isMirror(left, right),用于判断两个子树是否对称。 - 如果
left和right都为null,返回true。 - 如果
left或right其中一个为null,返回false。 - 如果
left.val不等于right.val,返回false。 - 递归判断
left.left和right.right是否对称,以及left.right和right.left是否对称。 - 如果上述两个条件都满足,返回
true,否则返回false。
迭代方法(使用队列):
- 创建一个队列,将根节点的左右子节点入队。
- 当队列不为空时:
- 取出队首的两个节点
left和right。 - 如果
left和right都为null,继续下一轮循环。 - 如果
left或right其中一个为null,或者left.val不等于right.val,返回false。 - 将
left.left和right.right入队。 - 将
left.right和right.left入队。
- 取出队首的两个节点
- 如果队列为空,返回
true。
算法可视化
以示例 1 为例,root = [1,2,2,3,4,4,3]:
递归方法:
- 调用
isMirror(root.left, root.right),即isMirror(2, 2)。 - 检查
2 == 2,条件满足。 - 递归调用
isMirror(2.left, 2.right),即isMirror(3, 3)。- 检查
3 == 3,条件满足。 - 递归调用
isMirror(3.left, 3.right)和isMirror(3.right, 3.left),都返回true。
- 检查
- 递归调用
isMirror(2.right, 2.left),即isMirror(4, 4)。- 检查
4 == 4,条件满足。 - 递归调用
isMirror(4.left, 4.right)和isMirror(4.right, 4.left),都返回true。
- 检查
- 所有条件都满足,返回
true。
迭代方法(使用队列):
- 将
root.left和root.right入队:队列 = [2,2]。 - 取出
2和2,检查2 == 2,条件满足。 - 将
2.left和2.right入队,以及2.right和2.left入队:队列 = [3,3,4,4]。 - 取出
3和3,检查3 == 3,条件满足。 - 将
3.left和3.right入队,以及3.right和3.left入队:队列 = [4,4,null,null,null,null]。 - 取出
4和4,检查4 == 4,条件满足。 - 将
4.left和4.right入队,以及4.right和4.left入队:队列 = [null,null,null,null,null,null,null,null]。 - 取出
null和null,继续循环。 - 最终队列为空,返回
true。
代码实现
C#
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
// 方法一:递归
public bool IsSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return IsMirror(root.left, root.right);
}
private bool IsMirror(TreeNode left, TreeNode right) {
// 如果两个节点都为空,则对称
if (left == null && right == null) {
return true;
}
// 如果其中一个节点为空,则不对称
if (left == null || right == null) {
return false;
}
// 如果两个节点的值不相等,则不对称
if (left.val != right.val) {
return false;
}
// 递归判断:左节点的左子树和右节点的右子树是否对称,以及左节点的右子树和右节点的左子树是否对称
return IsMirror(left.left, right.right) && IsMirror(left.right, right.left);
}
// 方法二:迭代(使用队列)
public bool IsSymmetricIterative(TreeNode root) {
if (root == null) {
return true;
}
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root.left);
queue.Enqueue(root.right);
while (queue.Count > 0) {
TreeNode left = queue.Dequeue();
TreeNode right = queue.Dequeue();
// 如果两个节点都为空,继续下一轮循环
if (left == null && right == null) {
continue;
}
// 如果其中一个节点为空,或者两个节点的值不相等,则不对称
if (left == null || right == null || left.val != right.val) {
return false;
}
// 将左节点的左子树和右节点的右子树入队
queue.Enqueue(left.left);
queue.Enqueue(right.right);
// 将左节点的右子树和右节点的左子树入队
queue.Enqueue(left.right);
queue.Enqueue(right.left);
}
return true;
}
}
Python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
# 方法一:递归
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
return self.isMirror(root.left, root.right)
def isMirror(self, left: Optional[TreeNode], right: Optional[TreeNode]) -> bool:
# 如果两个节点都为空,则对称
if not left and not right:
return True
# 如果其中一个节点为空,则不对称
if not left or not right:
return False
# 如果两个节点的值不相等,则不对称
if left.val != right.val:
return False
# 递归判断:左节点的左子树和右节点的右子树是否对称,以及左节点的右子树和右节点的左子树是否对称
return self.isMirror(left.left, right.right) and self.isMirror(left.right, right.left)
# 方法二:迭代(使用队列)
def isSymmetricIterative(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
from collections import deque
queue = deque([root.left, root.right])
while queue:
left = queue.popleft()
right = queue.popleft()
# 如果两个节点都为空,继续下一轮循环
if not left and not right:
continue
# 如果其中一个节点为空,或者两个节点的值不相等,则不对称
if not left or not right or left.val != right.val:
return False
# 将左节点的左子树和右节点的右子树入队
queue.append(left.left)
queue.append(right.right)
# 将左节点的右子树和右节点的左子树入队
queue.append(left.right)
queue.append(right.left)
return True
C++
/**
* 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) {
if (root == nullptr) {
return true;
}
return isMirror(root->left, root->right);
}
private:
bool isMirror(TreeNode* left, TreeNode* right) {
// 如果两个节点都为空,则对称
if (left == nullptr && right == nullptr) {
return true;
}
// 如果其中一个节点为空,则不对称
if (left == nullptr || right == nullptr) {
return false;
}
// 如果两个节点的值不相等,则不对称
if (left->val != right->val) {
return false;
}
// 递归判断:左节点的左子树和右节点的右子树是否对称,以及左节点的右子树和右节点的左子树是否对称
return isMirror(left->left, right->right) && isMirror(left->right, right->left);
}
public:
// 方法二:迭代(使用队列)
bool isSymmetricIterative(TreeNode* root) {
if (root == nullptr) {
return true;
}
queue<TreeNode*> q;
q.push(root->left);
q.push(root->right);
while (!q.empty()) {
TreeNode* left = q.front();
q.pop();
TreeNode* right = q.front();
q.pop();
// 如果两个节点都为空,继续下一轮循环
if (left == nullptr && right == nullptr) {
continue;
}
// 如果其中一个节点为空,或者两个节点的值不相等,则不对称
if (left == nullptr || right == nullptr || left->val != right->val) {
return false;
}
// 将左节点的左子树和右节点的右子树入队
q.push(left->left);
q.push(right->right);
// 将左节点的右子树和右节点的左子树入队
q.push(left->right);
q.push(right->left);
}
return true;
}
};
执行结果
C#
- 执行用时:84 ms,击败了 93.33% 的 C# 提交
- 内存消耗:39.8 MB,击败了 90.00% 的 C# 提交
Python
- 执行用时:36 ms,击败了 92.31% 的 Python3 提交
- 内存消耗:16.2 MB,击败了 88.46% 的 Python3 提交
C++
- 执行用时:4 ms,击败了 95.24% 的 C++ 提交
- 内存消耗:16.3 MB,击败了 91.67% 的 C++ 提交
代码亮点
- 简洁的递归实现:递归方法的实现非常简洁,易于理解,体现了树问题解决的典型思路。
- 迭代方法的灵活性:提供了迭代方法作为递归的替代,避免了递归可能导致的栈溢出问题。
- 提前返回:在发现不满足条件时立即返回,避免了不必要的计算。
- 清晰的逻辑结构:代码结构清晰,逻辑易于理解,特别是在处理空节点和值比较方面。
常见错误分析
- 忽略空节点检查:在比较两个子树时,忘记检查节点是否为空,可能导致空指针异常。
- 递归终止条件错误:递归方法中的终止条件不正确,可能导致无限递归或错误的结果。
- 队列使用不当:在迭代方法中,队列的使用不当,如忘记将节点的子节点入队,或者入队顺序错误。
- 混淆对称条件:对称二叉树要求左子树的左子节点和右子树的右子节点对称,左子树的右子节点和右子树的左子节点对称,容易混淆这个条件。
解法比较
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 递归 | O(n) | O(h),h为树的高度 | 实现简单,代码简洁 | 对于深度很大的树,可能导致栈溢出 |
| 迭代(队列) | O(n) | O(n) | 避免了递归可能导致的栈溢出问题 | 实现稍复杂,需要额外的队列空间 |