1 问题
给定一个二叉树,检查它是否是镜像对称的。
2 解法
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了「其实我们要比较的是两个树(这两个树是根节点的左右子树)」,所以在递归遍历的过程中,也是要同时遍历两棵树。
那么如果比较呢?
比较的是两个子树的里侧和外侧的元素是否相等。如图所示:
2.1 递归法
(1) 确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
返回值自然是bool类型。
代码如下:
bool compare(TreeNode* left, TreeNode* right)
(2) 确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:
左节点为空,右节点不为空,不对称,return false
左不为空,右为空,不对称 return false
左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
代码如下:
if(left == nullptr && right != nullptr) return false;
else if(left != nullptr && right == nullptr) return false;
else if(left == nullptr && right == nullptr) return true;
else if(left->val != right->val) return false;
(3) 确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理左右节点都不为空,且数值相同的情况。
比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
如果左右都对称就返回true ,有一侧不对称就返回false 。
代码如下:
//比较左子树的外侧与右子树的外侧
bool outside = compare(left->left, right->right);
//比较左子树的内侧与右子树的内侧
bool inside = compare(left->right, right->left);
//内侧与外侧都相等,则左右子树对称
return outside && inside;
整体代码如下:
/**
* 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 compare(TreeNode* left, TreeNode* right)
{
if(left == nullptr && right != nullptr) return false;
else if(left != nullptr && right == nullptr) return false;
else if(left == nullptr && right == nullptr) return true;
else if(left->val != right->val) return false;
else //left->val == right->val
{
//比较左子树的外侧与右子树的外侧
bool outside = compare(left->left, right->right);
//比较左子树的内侧与右子树的内侧
bool inside = compare(left->right, right->left);
//内侧与外侧都相等,则左右子树对称
return outside && inside;
}
}
bool isSymmetric(TreeNode* root) {
if(root == nullptr) return true;
return compare(root->left, root->right);
}
};
2.2 迭代法
2.2.1 使用队列
通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等,如动画所示:
/**
* 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;
queue<TreeNode*> que;
que.push(root->left);
que.push(root->right);
while(!que.empty())
{
TreeNode* leftNode = que.front();
que.pop();
TreeNode* rightNode = que.front();
que.pop();
//左右节点都为空,则对称,继续
if(!leftNode && !rightNode)
{
continue;
}
//左右节点某个为空,另一个不为空,或左右节点的值不等
if(!leftNode || !rightNode || leftNode->val != rightNode-> val)
return false;
//左右节点值相等
//放入左右子树的外侧节点
que.push(leftNode->left);
que.push(rightNode->right);
que.push(leftNode->right);
que.push(rightNode->left);
}
return true;
}
};
2.2.2 使用栈
这个迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较,那么其实使用栈也是可以的。
/**
* 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;
stack<TreeNode*> st;
st.push(root->left);
st.push(root->right);
while(!st.empty())
{
TreeNode* leftNode = st.top();
st.pop();
TreeNode* rightNode = st.top();
st.pop();
//左右节点都为空,则对称,继续
if(!leftNode && !rightNode)
{
continue;
}
//左右节点某个为空,另一个不为空,或左右节点的值不等
if(!leftNode || !rightNode || leftNode->val != rightNode-> val)
return false;
//左右节点值相等
//放入左右子树的外侧节点
st.push(leftNode->left);
st.push(rightNode->right);
st.push(leftNode->right);
st.push(rightNode->left);
}
return true;
}
};