层序遍历
只使用二叉树无法完成层序遍历,需要借助其他数据结构,使用的是队列。首先,插入根结点,然后设置循环条件(一般来说,需要借助队列/堆栈实现的循环,循环条件都会包含这些数据结构)。记录遍历完每层后结点的数量(可以设置size为容器大小值,表示当前层结点的数量),size用于防止队列上本层结点与下一层结点混肴。
102. 二叉树的层序遍历
出现错误:
1.直接使用if(!root)而不是if(root!=NULL)
2.对于队列来说,获取队列头元素是front函数,而不是top
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> res;
if(root!=NULL) que.push(root);
while(!que.empty()){
vector<int> vec;
int size=que.size();
while(size--){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left)que.push(node->left);
if(node->right)que.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
107. 二叉树的层序遍历 II
对比前面只需要添加一个翻转函数
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> res;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
while(size--){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
reverse(res.begin(),res.end());
return res;
}
};
226. 翻转二叉树
层序遍历法
本题之前使用层序遍历的方式,还是使用size记录当前层的结点数量,然后当队列加入下一层子结点后,将队列弹出的node的左右子节点交换,用来实现结点交换
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
if(root)que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* node=que.front();
TreeNode* tmp;
que.pop();
if(node->right)que.push(node->right);
if(node->left)que.push(node->left);
tmp=node->left;
node->left=node->right;
node->right=tmp;
}
}
return root;
}
};
递归法
还有一种使用递归的方法进行翻转,需要掌握递归的方法,首先确定递归函数的参数和返回值,由题目可得参数类型以及返回值,然后确定循环终止条件,当此时结点为空时跳出当前层,然后再根据递归逻辑确立。本题可以使用前序遍历和后序遍历,直接使用中序遍历时会出错,因为中序遍历会照成结点翻转两次。
在本题中,通过打印结果可以看到递归遍历的结点,首先将左右对换后,再遍历对换后的左结点,然后再右结点所以输出出来结果是反着的。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL)return root;
cout<<root->val<<" ";
swap(root->left,root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
101. 对称二叉树
层序遍历法
使用层序遍历可以完成,在层序遍历中,第一次不需要将头结点放入队列中,而是可以将其子节点放入队列中,然后根据队列上面左右节点判断其是否存在,是否相同,最后再将子节点中的子结点按照对称的顺序放入队列中即可。
class Solution {
public:
bool isSymmetric(TreeNode* root) {
queue<TreeNode*> que;
if(root==NULL)return true;
que.push(root->left); // 将左子树头结点加入队列
que.push(root->right); // 将右子树头结点加入队列
while(!que.empty()){
TreeNode* left=que.front();
que.pop();
TreeNode* right=que.front();
que.pop();
if(!left&&!right)continue;
if ((!left || !right || (left->val != right->val))) {
return false;//要分析好left和right的情况,因为不可能同时为0,所以只要有一个为0,就会返回false
}
que.push(left->left);
que.push(right->right);
que.push(left->right);
que.push(right->left);
}
return true;
}
};
递归法
确定递归顺序。本题使用后序,使用后序遍历的情况:收集孩子信息向上一结点返回。
1.首先确定函数参数类型以及返回值:因为要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树(因此确定函数参数类型为bool),所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
2.判断终止条件:
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
- 左右不为空,但是值不同,return false
剩下的就是左右节点都不为空,且数值相同的情况,然后以此进行判断
3.确定单层递归的逻辑
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
class Solution {
public:
bool compare(TreeNode* left,TreeNode* right){
if(!left&&right)return false;
else if(left&&!right)return false;
else if(!left&&!right)return true;
else if(left->val!=right->val)return false;
bool outside=compare(left->left,right->right);//左
bool inside=compare(left->right,right->left);//右
return outside&&inside;//中
}
bool isSymmetric(TreeNode* root) {
if(!root)return true;
return compare(root->left,root->right);
}
};