本题的核心在于如何判定一颗树是不是镜像对称的呢?如果说一棵树的左孩子的左侧和右孩子的右侧是相等的,左孩子的右侧和右孩子的左侧是相等的,如此下去,这个数就是镜像对称的,所以,这个题目我们要根据这一个点进行判断。
本题还是使用递归法,提到递归法,我们就得心里有递归的三部曲:
bool symmetric(TreeNode* leftNode, TreeNode* rightNode)
if (node == nullptr) return;
,但是这个题明显就不是。ans.empalce_back(node->val);
traversal(node->left);
traversal(node->right);
这每一条语句都有一个大前提,那就是node不能为空,node要是为空的话,每一句话都会报错,所以这就是为什么要寻找递归的终止条件。
我们回到这个题目,很明显,我们进行递归的时候的处理如下:
bool outSide = symmetric(leftNode->left, rightNode->right); // 外侧进行判定
bool inSide = symmetric(leftNode->right, rightNode->left); // 内侧进行判定
这里要判定就是左孩子的左结点和右孩子的右结点,那么前提是什么呢,肯定要是这两个结点不为空啊,如果为空,不是直接就报错了吗,所以递归的终止条件就是在这个上面做文章。那么这两个结点是否为空一共有四种情况:
if (!leftNode && !rightNode) return true; // 左右都是空,这个树肯定是对称的
else if (leftNode && !rightNode) return false; // 左结点为空,右结点不为空,肯定不是对称的
else if (!leftNode && rightNode) return false; // 同理
else if (leftNode->val != rightNode->val) return false; // 都不为空,但是值不相等
所以递归函数为:
bool symmetric(TreeNode* leftNode, TreeNode* rightNode) {
// 这个递归的终止条件有一点复杂,一定要注意你这个递归函数是干什么的
if (!leftNode && !rightNode) return true; // 左右都是空,这个树肯定是对称的
else if (leftNode && !rightNode) return false; // 左结点为空,右结点不为空,肯定不是对称的
else if (!leftNode && rightNode) return false; // 同理
else if (leftNode->val != rightNode->val) return false; // 都不为空,但是值不相等
// 接下来就是需要递归的情况了
bool outSide = symmetric(leftNode->left, rightNode->right); // 外侧进行判定
bool inSide = symmetric(leftNode->right, rightNode->left); // 内侧进行判定
return outSide && inSide;
}
递归法:
class Solution {
public:
bool symmetric(TreeNode* leftNode, TreeNode* rightNode) {
// 这个递归的终止条件有一点复杂,一定要注意你这个递归函数是干什么的
if (!leftNode && !rightNode) return true; // 左右都是空,这个树肯定是对称的
else if (leftNode && !rightNode) return false; // 左结点为空,右结点不为空,肯定不是对称的
else if (!leftNode && rightNode) return false; // 同理
else if (leftNode->val != rightNode->val) return false; // 都不为空,但是值不相等
// 接下来就是需要递归的情况了
bool outSide = symmetric(leftNode->left, rightNode->right); // 外侧进行判定
bool inSide = symmetric(leftNode->right, rightNode->left); // 内侧进行判定
return outSide && inSide;
}
bool isSymmetric(TreeNode* root) {
// 判断对称其实就是判断左右子树是不是对称的
TreeNode* leftNode = root->left;
TreeNode* rightNode = root->right;
return symmetric(leftNode, rightNode);
}
};
当然本题使用迭代法也是可以解决的。
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; // 注意这个时候千万不可以return,因为只能说明外侧是对称的,内侧的情况还不知道呢
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;
}
};
迭代法使用的是队列来实现的,需要注意的有一个点:
leftNode == nullptr && rghtNode == nullptr
的时候,这个时候不能直接return,因为此时只能判断外侧是对称的,内测是什么情况完全不知道。本题考查求二叉树的最大深度,首先要明确深度的定义。对于这个题目,我们可以使用前序遍历,后序遍历以及层序遍历都是可以做的。
使用前序遍历才是真正的求深度,里面有回溯的思想;使用后序遍历求的其实是高度,也是可以接受;使用层序遍历的话直接用模板,最后看看有几个vector即可。
首先来分析后序遍历:
int getDepth(TreeNode* node)
if (node == nullptr) return 0;
,这里的含义就是,此时node已经是空结点了,所以相应的高度就是0,这是可以的。if (!node->left && !node->right) return 1;
,因为当前的是叶子结点,所以这里的高度就是1,此时就可以返回1,所以终止条件改成这个也是合理的。if (node == nullptr) return 0;
if (!node->left && !node->right) return 1;
但是其实这样的话,第二个其实就可要可不要了,但是这个也反映了我们思考的过程。
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
return 1 + max(leftDepth, rightDepth); // 需要加上当前结点,所以需要加1
层序遍历:
本题使用层序遍历就直接套模板就行了,最后返回vector的个数即可。
前序遍历的思路分析:
前序遍历可以参看这里的总结。
后序遍历:
class Solution {
public:
int getDepth(TreeNode* node) {
// 求深度可以使用后序遍历也可以使用前序遍历
// 使用后序遍历本质上求的是高度
if (!node) return 0;
if (!node->left && !node->right) return 1; // 这里我两个条件都写了
// 这里先使用后序遍历
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
return 1 + max(leftDepth, rightDepth); // 需要加上当前结点,所以需要加1
}
int maxDepth(TreeNode* root) {
return getDepth(root);
}
};
层序遍历:
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
// 说白了就是找层数,可以使用广度优先搜索
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
depth++; // 遍历完一层深度加1
}
return depth;
}
};
题目分析:
本题和二叉树的最大深度非常类似,但是有一个地方很容易出错。
图片来源
就是如果根节点只有左孩子或者右孩子的情况,最小的深度不是1
题目解答:
后序递归法:
class Solution {
public:
int getDepth(TreeNode* node) {
if (node == nullptr) return 0;
// 注意处理根节点只有左子树或只有右子树的情况
if (!node->left && node->right) return 1 + getDepth(node->right); // 左子树没有的情况
if (node->left && !node->right) return 1 + getDepth(node->left); // 右子树没有的情况
// 正常的情况
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
return 1 + min(leftDepth, rightDepth);
}
int minDepth(TreeNode* root) {
return getDepth(root);
}
};
层序遍历法:
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*> que;
que.push(root);
int depth = 1;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
// 注意这里使用了node的left和right,所以我们要保证node不为空
if (!node->left && !node->right) return depth; // 如果当前结点为叶子结点,直接返回即可
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
depth++;
}
return depth;
}
};
这里第二个题的前序遍历的解法涉及到回溯和递归的配合,和第513题找树左下角的值非常之类似。还与层序遍历的递归解法以及257题二叉树的所有路径相似,我们到时候一起总结一下这几个。我们在这里进行了总结。