一、题目打卡
直接上 10 道题目,因为基本都是层序遍历的思路,之前也接触过,就直接开始写了:
1.1 层序遍历
题目链接:. - 力扣(LeetCode)
class Solution {
private:
vector<int> tmp_;
vector<vector<int>> res;
queue<TreeNode*> q;
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
tmp_.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
res.push_back(tmp_);
tmp_.clear();
}
return res;
}
};
思路整体是比较简单的,主要就是动态维护一个 queue 队列,队列里面存储的是每一层树的节点,然后按照这个动态 queue 的 size 进行一个遍历输出就可以了。
1.2 二叉树的层序遍历II
其实就是多了一个结果的逆序:
class Solution {
private:
vector<int> _tmp;
vector<vector<int>> _res;
queue<TreeNode*> q;
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
_tmp.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
_res.push_back(_tmp);
_tmp.clear();
}
reverse(_res.begin(),_res.end());
return _res;
}
};
1.3 二叉树的右视图
对于这个题目而言,由于事先不知道二叉树的形状,如果二叉树是一个满二叉树,那么其实递归可以很好地解决:
class Solution {
private:
void recur(TreeNode* root, vector<int> &res){
if(!root) return;
res.push_back(root->val);
recur(root->right,res);
return;
}
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
recur(root,res);
return res;
}
};
但是由于本身二叉树的形状不确定,因而持续取右会出错。因而还是根据层序遍历,然后取末尾的值比较好:
class Solution {
private:
vector<int> _tmp;
vector<vector<int>> _res;
queue<TreeNode*> q;
public:
vector<int> rightSideView(TreeNode* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
_tmp.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
_res.push_back(_tmp);
_tmp.clear();
}
vector<int> res;
for(auto& it : _res){
res.push_back(it.back());
}
return res;
}
};
1.4 二叉树的层平均值
leetcode链接:. - 力扣(LeetCode)
class Solution {
private:
vector<int> _tmp;
vector<vector<int>> _res;
queue<TreeNode*> q;
double sum(vector<int> & nums){
double res = 0;
for(auto &it : nums){
res += it;
}
return res;
}
public:
vector<double> averageOfLevels(TreeNode* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
_tmp.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
_res.push_back(_tmp);
_tmp.clear();
}
vector<double> res;
for(auto& it : _res){
res.push_back((double)sum(it)/it.size());
}
return res;
}
};
没什么特别的,注意结果的数据转换。
贴一个答案的深度优先搜索:
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
auto counts = vector<int>();
auto sums = vector<double>();
dfs(root, 0, counts, sums);
auto averages = vector<double>();
int size = sums.size();
for (int i = 0; i < size; i++) {
averages.push_back(sums[i] / counts[i]);
}
return averages;
}
void dfs(TreeNode* root, int level, vector<int> &counts, vector<double> &sums) {
if (root == nullptr) {
return;
}
if (level < sums.size()) {
sums[level] += root->val;
counts[level] += 1;
} else {
sums.push_back(1.0 * root->val);
counts.push_back(1);
}
dfs(root->left, level + 1, counts, sums);
dfs(root->right, level + 1, counts, sums);
}
};
1.5 N叉树的层序遍历
leetcode链接:. - 力扣(LeetCode)
class Solution {
private:
vector<vector<int>> _res;
vector<int> _tmp;
queue<Node*> q;
public:
vector<vector<int>> levelOrder(Node* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
Node* tmp = q.front();
q.pop();
if(tmp){
_tmp.push_back(tmp->val);
for(auto & it : tmp->children){
if(tmp) q.push(it);
}
}
}
_res.push_back(_tmp);
_tmp.clear();
}
return _res;
}
};
1.6 在树的每一行寻找最大值
leetcode题目链接:. - 力扣(LeetCode)
class Solution {
private:
vector<int> tmp_;
vector<vector<int>> res;
queue<TreeNode*> q;
public:
vector<int> largestValues(TreeNode* root) {
if(!root) return {};
q.push(root);
while(!q.empty()){
int size = q.size();
while(size--){
TreeNode* tmp = q.front();
q.pop();
tmp_.push_back(tmp->val);
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
res.push_back(tmp_);
tmp_.clear();
}
vector<int> ans;
for(auto &it : res){
auto m = max_element(it.begin(),it.end());
ans.push_back(*m);
}
return ans;
}
};
本身在第二个while循环的逻辑进行处理就可以,随时更新最大值的大小,不过时间原因,直接复用的之前的代码,并使用 max_element 方法直接返回。
这个题目如果采用的是递归的话,可以变的很简洁:
class Solution {
private:
void recur(TreeNode* root, int level, vector<int> &res){
if(level == res.size()) res.push_back(root->val); // 说明开辟出了新的深度
else res[level] = max(res[level],root->val);
if(root->left) recur(root->left,level+1,res);
if(root->right) recur(root->right,level+1,res);
}
public:
vector<int> largestValues(TreeNode* root) {
if(!root) return {};
vector<int> res;
recur(root,0,res);
return res;
}
};
1.7 填充每个节点的下一个右侧节点指针(答案更简洁)
题目链接:. - 力扣(LeetCode)
class Solution {
private:
vector<Node*> _tmp;
vector<vector<Node*>> _res;
queue<Node*> _q;
public:
Node* connect(Node* root) {
if(!root) return root;
_q.push(root);
while(!_q.empty()){
int size = _q.size();
while(size--){
Node* tmp = _q.front();
_q.pop();
_tmp.push_back(tmp);
if(tmp->left) _q.push(tmp->left);
if(tmp->right) _q.push(tmp->right);
}
_res.push_back(_tmp);
_tmp.clear();
}
for(auto& it: _res){
for(int i = 0; i < it.size() - 1;i++){
if(it[i] && it[i+1]) it[i]->next = it[i+1];
}
it.back()->next = nullptr;
}
return root;
}
};
时间原因,继续复用,答案还是把处理的逻辑放在了里面。
1.8 填充右侧节点II
题目没有什么特别的,上个题的答案可以直接通过,这里就直接贴一个链接:. - 力扣(LeetCode)
1.9 二叉树最大深度
层序遍历没有什么特别的,依旧是进行队列的遍历模拟,只是不需要添加返回值了,用一个计数器记录深度,可以理解为正向的思想,这里我也就不贴答案了,尝试使用 dfs 进行求解,不过我使用的依然是正向的思维:
class Solution {
private:
int max_res = 0;
void recur(TreeNode* root,int level){
if(!root) return;
max_res = max(max_res,level);
if(root->left) recur(root->left,level+1);
if(root->right) recur(root->right,level+1);
}
public:
int maxDepth(TreeNode* root) {
if(!root) return 0;
recur(root,1);
return max_res;
}
};
不过其实可以和答案一样,采用逆向的思维,可以更简洁:
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
1.10 二叉树的最小深度
题目链接:. - 力扣(LeetCode)
class Solution {
public:
int minDepth(TreeNode* root) {
if(!root) return 0;
int m = minDepth(root->left);
int n = minDepth(root->right);
if(m == 0 || n == 0) return m + n + 1;
else return min(m,n) + 1;
}
};
思考这个题目的时候,还是踩的一些坑的,这里最小深度的理解需要注意一点,对于那种只有一段有子树的情况,这一个节点并不是叶子节点,而且不要去思考递归的过程,容易陷进去,还是以每一个过程进行思考:
对于每一个递归后的返回值,其实对应的就是两个子树的深度,而非叶子节点,返回的应该是含有子树那一端的最小子树大小,而不是直接返回 1,这是我踩的坑,就是我标记的位置,记录一下:
思考这个问题的时候,也应该把一个单独的位置拿出来进行思考,就是一个三角的小结构,分析这个问题,也可以把整个大问题分为小问题:左子树的最小深度和右子树的最小深度,而需要返回的是应该是这两个值的最小值(在两边都存在的时候),如果有一边不存在,那么应该返回的是存在的那一边的数值,最后一种情况就是叶子节点,这时候直接返回 0 就可以了。
1.11 翻转二叉树
题目链接:. - 力扣(LeetCode)
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return nullptr;
TreeNode* left = invertTree(root->right);
TreeNode* right = invertTree(root->left);
root->left = left;
root->right = right;
return root;
}
};
这个题目,第一次写的时候出现了情况:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return nullptr;
root->left = invertTree(root->right);
root->right = invertTree(root->left);
return root;
}
};
这个代码是错误的,虽然没有太想明白,大概的感觉是,如果在第一步就直接改了左节点的索引,那么在下一步调用左索引的时候,是会出现错误的,所以正确的做法应该是把这两个返回值存起来,在处理完所有的节点了以后再统一进行索引的修改。
1.12 对称二叉树
题目链接:. - 力扣(LeetCode)
class Solution {
private:
// TreeNode* reverseTree(TreeNode* root){
// if(!root) return nullptr;
// TreeNode* left = reverseTree(root->left);
// TreeNode* right = reverseTree(root->right);
// root->left = right;
// root->right = left;
// return root;
// }
// bool j(TreeNode* root1, TreeNode* root2){
// if (!root1 && !root2) return true;
// if (!root1 || !root2) return false;
// // return root1->val == root2->val && j(root1->left,root2->left) && j(root1->right,root2->right); // 这个是判断两个是否完全相等,但是相等不一定对称
// }
bool judge(TreeNode* root1, TreeNode* root2){
if(!root1 && !root2) return true;
if(!root1 || !root2) return false;
return root1->val == root2->val && judge(root1->left,root2->right) && judge(root1->right,root2->left);
}
public:
bool isSymmetric(TreeNode* root) {
// if(!root) return true;
// if(root->left->val != root->right->val) return false;
// return root->left->left->val == root->right->right->val && root->left->right->val == root->right->left->val;
// TreeNode* reversedTree = reverseTree(root);
if(!root) return true;
if(root && !root->left && !root->right) return true;
TreeNode* left = root->left;
TreeNode* right = root->right;
return judge(left,right);
}
};
这个题目在做的时候还踩了挺多坑的,首先是这个判断的条件,应该是头结点的两个子树,递归的不应该是左右节点,第二个就是判断的是对称,而不是两个是否相等,一开始考虑成两个是否相等了(想法本来是把原本的进行翻转,然后看两个是否相等),但是这样只能针对满二叉树,最后就是找到正确思路的时候,需要谨慎处理为空的情况。