问题描述
给定一个二叉树的根节点 root,检查它是否为轴对称的。轴对称的意思是,这棵树的左子树和右子树在形状和节点值上都是镜像对称的。
思路分析
要判断一棵树是否对称,我们需要检查它的左子树和右子树是否互为镜像。这可以通过递归和迭代两种方法实现。具体来说,判断两个树是否互为镜像需要满足以下条件:
1. 两棵树的根节点值相等。
2. 每棵树的右子树和另一棵树的左子树互为镜像。
我们可以利用上述性质来编写递归和迭代算法。
方法一:递归
思路与算法
递归的方法利用了树的天然递归性质。我们定义一个辅助函数 check,它接收两个节点 p 和 q,并检查它们是否互为镜像:
1. 基础情况:
- 如果两个节点都为空,返回 true(空树是对称的)。
- 如果只有一个节点为空,返回 false(结构不同)。
- 如果两个节点的值不同,返回 false。2. 递归调用:
- check(p->left, q->right) 检查 p 的左子树和 q 的右子树是否对称。
- check(p->right, q->left) 检查 p 的右子树和 q 的左子树是否对称。
代码实现
class Solution {
public:
// 辅助函数:判断两个子树是否互为镜像
bool check(TreeNode *p, TreeNode *q) {
if (!p && !q) return true; // 两个节点都为空
if (!p || !q) return false; // 只有一个节点为空
// 根节点值相同,并且左右子树互为镜像
return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
}
// 主函数:判断整个树是否对称
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的节点总数。我们每个节点访问一次。
- 空间复杂度:O(n),最坏情况下(树的高度等于节点数),递归调用的栈深度会达到 O(n)。
优缺点
递归方法的优点在于代码简洁,逻辑清晰,易于理解和实现。然而,递归的缺点在于对于深度大的树,可能会导致栈溢出,特别是在树的高度等于节点数的最坏情况下。
方法二:迭代
思路与算法
迭代方法使用队列来替代递归栈。我们初始化一个队列,将根节点入队两次。每次从队列中取出两个节点,并进行以下操作:
1. 如果两个节点都为空,继续下一次循环。
2. 如果其中一个节点为空或两个节点的值不同,返回 false。
3. 将两个节点的左右子节点按对称顺序加入队列:u->left 和 v->right,u->right 和 v->left。
代码实现
class Solution {
public:
bool check(TreeNode *u, TreeNode *v) {
queue<TreeNode*> q;
q.push(u);
q.push(v);
while (!q.empty()) {
u = q.front(); q.pop();
v = q.front(); q.pop();
// 如果两个节点都为空,继续
if (!u && !v) continue;
// 如果只有一个节点为空或节点值不同,返回 false
if ((!u || !v) || (u->val != v->val)) return false;
// 按镜像顺序加入子节点
q.push(u->left);
q.push(v->right);
q.push(u->right);
q.push(v->left);
}
return true;
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的节点总数。每个节点最多访问两次。
- 空间复杂度:O(n),队列中最多存储 n 个节点。
优缺点
迭代方法的优点在于可以避免递归带来的栈溢出问题,特别适合处理深度大的树。同时,迭代方法使用队列管理节点,逻辑上更接近于层次遍历的方式。然而,缺点是队列占用的空间可能较大,特别是在树的宽度较大时,空间消耗可能更高。
总结
判断二叉树是否对称是一个经典问题,常用的解法包括递归和迭代。递归方法自然直观,适合初学者理解,但可能会有栈溢出的问题。迭代方法通过队列来实现遍历,避免了栈溢出,适合处理深度大的树。