226.翻转二叉树
翻转每一个结点的左右孩子就能达到整体翻转的效果。
前后遍历都可以,但中序遍历不可以。
1.前序遍历(递归法)
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;
}
};
当然,swap语句写在两个递归后面也可以,这样就变成后序遍历了。中序遍历为什么不行,因为 有可能遍历左子树时左右子树交换之后,遍历右子树时该树已经变成原先的左子树。会导致一棵树交换两次或有树没有交换的情况。
2.迭代法
深度优先遍历
前序遍历(统一迭代法)
动图理解
前序遍历的过程:
先访问节点本身(压栈后再弹出),再访问其左、右子树,最后交换左右子树。
就是用栈模拟递归的过程。
if语句那里表示:如果
node
不为空:
- 弹出当前节点。
- 依次将其右子节点和左子节点压入栈中(前序遍历顺序:先处理根,再左子树,最后右子树,但为了最终反转,需要先压入右子树再左子树)。
- 将当前节点再次压入栈中,后面再处理它。
- 压入一个
NULL
占位符,标志当前节点的子节点已经处理完,稍后会处理它本身。
else语句:如果当前栈顶是
NULL
,这意味着已经处理完了该节点的左右子节点,现在需要反转它的左右子树。
- 弹出栈顶的
NULL
占位符。- 再次取出栈顶节点
node
,并弹出它。- 使用
swap
函数交换该节点的左子树和右子树,完成该节点的反转操作。现在理解了这种解法。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
st.push(node); // 中
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
swap(node->left, node->right); // 节点处理逻辑
}
}
return root;
}
};
层序遍历
(广度优先遍历)喜欢这个,不知道是不是因为写多了。。。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*>que;
if(root!=NULL)que.push(root);
while(!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
swap(node->left, node->right); // 节点处理
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return root;
}
};
101.对称二叉树
递归法
终止条件
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
剩下一直左右不为空数值相等的情况就进入单层递归逻辑。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
一种不算严格的后序遍历,左子树左右中,右子树右左中。
代码
class Solution {
public:
bool compare(TreeNode*left,TreeNode*right)
{
//终止条件
if(left!=0&&right==0)return false;
else if(left==0&&right!=0)return false;
else if(left==0&&right==0)return true;
else if(left->val!=right->val)return false;
bool outsize=compare(left->left,right->right);//如果左右不为空且数值相等则进入递归逻辑
bool insize=compare(left->right,right->left);
int issame=outsize&&insize;
return issame;
}
bool isSymmetric(TreeNode* root) {
if(root==NULL)return true;
return compare(root->left,root->right);
}
};
迭代法
1.使用栈
和队列差不多写法,区别就是左右子树存放顺序略有差别
2.使用队列
class Solution {
public:
bool isSymmetric(TreeNode* root) {//队列迭代法
if(root==NULL)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;
}
};
104.二叉树的最大深度
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
后序遍历:
class Solution {
public:
int getnode(TreeNode* node)
{
if(node==NULL)return 0;
int leftdepth=getnode(node->left);//左
int rightdepth=getnode(node->right);//右
int depth= 1+max(leftdepth,rightdepth);//中
return depth;
}
int maxDepth(TreeNode* root) {
return getnode(root);
}
};
前序遍历:
class Solution {
public:
int result;//全局变量
void getnode(TreeNode* node,int depth)
{
result=result>depth?result:depth;//中
if(node->left==NULL&&node->right==NULL)return;
if(node->left)getnode(node->left,depth+1);//左
if(node->right)getnode(node->right,depth+1);//右
return ;
}
int maxDepth(TreeNode* root) {
result=0;
if(root==NULL)return 0;
getnode(root,1);
return result;
}
};
层序遍历前文写过。
111.二叉树的最小深度
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
遍历的顺序为后序(左右中),可以看出:求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。
1.后序遍历递归法
class Solution {
public:
int getdepth(TreeNode*node)
{
if(node==NULL)return 0;
int leftdepth=getdepth(node->left);//左
int rightdepth=getdepth(node->right);//右
if(node->left==NULL&&node->right!=NULL)//当一棵树左为空,右不为空,还不是最低点。
return 1+rightdepth;
if(node->left!=NULL&&node->right==NULL)
return 1+leftdepth;
int result=1+min(leftdepth,rightdepth);
return result;
}
int minDepth(TreeNode* root) {
return getdepth(root);
}
};
精简版
class Solution { public: int minDepth(TreeNode* root) { if (root == NULL) return 0; if (root->left == NULL && root->right != NULL) { return 1 + minDepth(root->right); } if (root->left != NULL && root->right == NULL) { return 1 + minDepth(root->left); } return 1 + min(minDepth(root->left), minDepth(root->right)); } };
2.前序遍历递归法
class Solution {
private:
int result;
void getdepth(TreeNode* node, int depth) {
// 函数递归终止条件
if (node == nullptr) {
return;
}
// 中,处理逻辑:判断是不是叶子结点
if (node -> left == nullptr && node->right == nullptr) {
result = min(result, depth);
}
if (node->left) { // 左
getdepth(node->left, depth + 1);
}
if (node->right) { // 右
getdepth(node->right, depth + 1);
}
return ;
}
public:
int minDepth(TreeNode* root) {
if (root == nullptr) {
return 0;
}
result = INT_MAX;
getdepth(root, 1);
return result;
}
};
和最大深度差不多,区别就在于一个结点左子树空右子树不空不一定找到最低点。
3.迭代法--层序遍历,前文写过。
总结。三步递归,迭代,层序遍历。