226.翻转二叉树 (优先掌握递归)
遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。
递归法:
- 确定递归函数的参数和返回值
- 确定终止条件:当前节点为空的时候,就返回
- 确定单层递归的逻辑:先交换左右孩子节点,再反转左子树,再反转右子树
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
迭代法
深度优先遍历:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return root;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* tmp = st.top();
st.pop();
swap(tmp->left, tmp->right);
if(tmp->right) st.push(tmp->right);
if(tmp->left) st.push(tmp->left);
}
return root;
}
};
广度优先遍历:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> q;
if(root!=NULL) q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
swap(tmp->left, tmp->right);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
}
return root;
}
};
101. 对称二叉树
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树)。那么如何比较呢?
比较的是两个子树的里侧和外侧的元素是否相等。
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
确定终止条件:
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 注意这里我没有使用else
注意上面最后一种情况,我没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。
确定单层递归的逻辑:
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
可以得出完整代码:
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right){
if(left==NULL&&right==NULL) return true; //全相等的话返回这个
else if(left!=NULL&&right==NULL) return false;
else if(left==NULL&&right!=NULL) return false;
else if(left->val!=right->val) return false; // 排除了空节点,再排除数值不相同的情况
bool outside = compare(left->left, right->right);
bool inside = compare(left->right, right->left);
bool result = outside&&inside;
return result;
}
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
return compare(root->left, root->right);
}
};
104.二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)从上往下
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)从下往上
从下往上遍历的时候,可以采用前后中(后序遍历),这个时候父节点+1,依次得到根节点
从上往下遍历的时候,可以采用中前后(前序遍历),这个时候子结点+1,依次得到叶子节点
根节点的高度=二叉树的深度,所以求最大深度,反而可以用后序遍历。
class Solution {
public:
int getheight(TreeNode* node){
if(node==NULL) return 0;
//单层递归逻辑
int leftheight = getheight(node->left);
int rightheight = getheight(node->right);
int height = 1+max(leftheight, rightheight);
return height;
}
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
return getheight(root);
}
};
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量
本题有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。代表没有左子树也没有右子树
在确定单层递归的逻辑的时候,本能性地想这么写:
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;
如果这么求的话,没有左孩子的分支会算为最短深度。
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度;反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
class Solution {
public:
int getheight(TreeNode* root){ //后序遍历得到根结点的高度,对应树的深度
if(root==NULL) return 0;
int leftheight = getheight(root->left);
int rightheight = getheight(root->right);
// 当一个左子树为空,右不为空,这时并不是最低点
if(root->left == NULL && root->right != NULL) return 1+rightheight;
// 当一个右子树为空,左不为空,这时并不是最低点
if(root->right == NULL && root->left != NULL) return 1+leftheight;
int result = 1 + min(leftheight, rightheight);
return result;
}
int minDepth(TreeNode* root) {
return getheight(root);
}
};