第十一天
102-二叉树的层序遍历
经典层序遍历,利用队列模拟,由于输出时是按照层序的二维数组,所以需要记录每层入队的数目
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root)
return {};
vector<vector<int>> ans;
queue<TreeNode * > q;
q.push(root);
//经典广搜模板
while(!q.empty())
{
int cnt = q.size();//记录入队数量
vector<int> temp;
while(cnt--)
{
TreeNode * cur = q.front();
q.pop();
temp.push_back(cur->val);
if(cur->left!=nullptr)q.push(cur->left);
if(cur->right!=nullptr)q.push(cur->right);
}
ans.push_back(temp);
temp.clear();//记得清空
}
return ans;
}
};
104-二叉树的最大深度
思路1:递归
最朴素的思路就是深搜,每次往下一层,则depth+1,直到到叶子节点,则更新最大depth
class Solution {
public:
void order(TreeNode * root, int depth, int & ans)
{
if(root==nullptr)
return;
order(root->left, depth+1, ans = max(ans, depth+1));
//利用ans记录最大depth
order(root->right, depth+1, ans = max(ans, depth+1));
}
int maxDepth(TreeNode* root) {
int ans = 0;
if(root==nullptr)
return ans;
order(root, 0, ans);
return ans;
}
};
简化版
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root==nullptr)
return 0;
//注意这里的递归写法
return max(maxDepth(root->left), maxDepth(root->right))+1;
}
};
思路2:广度搜索BFS
对于二叉树的遍历来说,也就是层序遍历,可以天然的记录层次也就是深度
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*> Q;
Q.push(root);
int ans = 0;
while (!Q.empty()) {
int sz = Q.size();
while (sz--) {
TreeNode* node = Q.front();Q.pop();
if (node->left) Q.push(node->left);
if (node->right) Q.push(node->right);
}
ans += 1;//记录深度
}
return ans;
}
};
101-对称二叉树
基础都是DFS——当然BFS也可以做,具体解法见LeetCode官网
思路1:利用flag带回结果
判断一颗二叉树是否对称,主要是两点:
- 对称节点的值相同
- 树型结构要对称
所以最朴素的思想就是设定两个指针去分别遍历左子树和右子树,对其结构和节点的值进行判断和比较,则遍历时应该遵循
对于左子树指针left和右子树指针right来说
left->left->val = right->right->val
left->right->val = right->left->val
同样需要注意空指针的特判
如何判断其对称?不妨把这个问题换一下:如何判断其不对称呢?显然从值和结构两个方面:
- 当对应节点的值不同时,则结构不同
- 当两个指针仅有一个指空时,说明其结构不同
当没有上述问题时,则说明该二叉树是对称的,这里我们利用flag来标志是否对称
class Solution {
public:
void Judge(TreeNode * left, TreeNode * right,int&flag)
{
//当左右指针中仅有一个为空,则其结构不对称
if((left==nullptr&&right!=nullptr)||(left!=nullptr&&right==nullptr))
{
flag = 1;
return;
}
//都为空,无法确定其一定对称,直接返回
else if(left==nullptr&&right==nullptr)
{
return;
}
//值不同一定不对称
else if(left->val!=right->val)
{
flag = 1;
return;
}
//两个指针按照对称的方向遍历
Judge(left->left, right->right, flag);
Judge(left->right, right->left, flag);
}
bool isSymmetric(TreeNode* root) {
//特判仅一个节点的情况
if(root->left==nullptr&&root->right==nullptr)
return true;
TreeNode * left = root->left;
TreeNode * right = root->right;
int flag = 0;
//根据flag判断其是否对称
Judge(left, right, flag);
if(flag)
return false;
else
return true;
}
};
思路2:优化递归条件
当然第一个思路的做法比较麻烦,利用flag进行结果的带回,我们可不可以将其优化一下?
考虑到对称成立的多个条件,我们可以将这些条件进行组合,放在一起进行最终结果的判断
- 仅从结构上来说,当前节点都存在,则结构对称;当前节点都不存在,则结构对称;当前节点只有一个存在,则结构不对称
- 仅从值上来说,当前节点值相同,结构对称
则把上述条件用&&并列,则可以得到最终是否对称的判断条件
class Solution {
public:
bool check(TreeNode * left, TreeNode * right)
{
if(!left&&!right)
return true;
if(!left||!right)
return false;
//注意这里条件判断的新写法从结构和值上分别进行对称的判断
return left->val == right->val && check(left->left, right->right) && check(left->right, right->left);
}
bool isSymmetric(TreeNode* root) {
if(root->left==nullptr&&root->right==nullptr)
return true;
TreeNode * left = root->left;
TreeNode * right = root->right;
return check(left, right);
}
};
第十二天
226-翻转二叉树
思路:对二叉树进行翻转,我们同样从深度搜索DFS的角度来考虑
从递归的角度来说,递归的边界是什么?递归的下一层是什么?
二叉树的翻转也就是对于每一个根节点来说将其左右子树进行互换
如果当前结点为NULL,则其没有左右子树,那么递归无法往下走,则该返回,这就是递归的边界条件;
当前结点存在,则其一定有左右子树,递归往其左右子树上走,这就是递归的下一层;
而交换左右子树的工作应该在递归的回溯上,从叶子往上依次进行交换。
理解了上述两点,则不难写出以下代码:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr)
return nullptr;
TreeNode * left = invertTree(root->left);
TreeNode * right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
112-路径总和
思路1:DFS
目标是找到一条从根节点到叶子结点的路径,使得路径上的值相加等于targetSum,那么我们需要明确两个问题:
- 如何判断当前的值和targetSum相等?
- 如何判断已经走到叶子结点?
同样这是一个多条件的递归问题,我们可以借鉴上面101-对称二叉树的思路,将条件进行并列,由于我们只需要存在一条路径即可,所以条件的并列应该使用或(这里和对称二叉树不一样,需要读者仔细思考)
同样从特殊结点的返回开始考虑:
- 对于空结点:其肯定不是叶子节点,直接返回false
- 对于叶子节点:如果前面的路径和加上当前叶子结点的值之和等于targetSum,则两个条件都满足,返回true;否则返回false
则有以下代码:
class Solution {
public:
bool check(TreeNode * root, int targetSum, int temp)
{
if(root==nullptr)//对于空节点
return false;
else if(root->left==nullptr&&root->right==nullptr)//叶子结点
{
if(targetSum==temp+root->val)
return true;
else
return false;
}
//只需要满足存在一条路径即可
return check(root->left,targetSum, temp+root->val) || check(root->right, targetSum, temp+root->val);//每次向下递归时,需要给temp加上当前结点的值
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==nullptr)
return false;
int cnt = 0;//用于计算当前路径值之和
return check(root, targetSum, cnt);
}
};
思路2:BFS
做过BFS相关题目的同学读完题目肯定很熟悉——这不就是变形的单源路径问题嘛?——是的,只是把矩阵换成了二叉树,把求最短变成了求相等。所以我们同样可以利用队列来模拟广度搜索,实现问题的解决。
我们需要两个队列分别存放结点和结点的累加和(也可以说当前路径的长度);
每次出队则判断其是否为叶子节点,是则将其值加上前面路径长度进行判断,相同则直接返回true;
将出队节点(非叶子节点)的左右子树节点入队,同时更新当前路径的长度
直到队列为空
这里直接借鉴leetCode官网的代码进行讲解
class Solution {
public:
bool hasPathSum(TreeNode *root, int sum) {
if (root == nullptr) {
return false;
}
queue<TreeNode *> que_node;
queue<int> que_val;
que_node.push(root);
que_val.push(root->val);
while (!que_node.empty()) {
//出队取值
TreeNode *now = que_node.front();
int temp = que_val.front();
que_node.pop();
que_val.pop();
//叶子节点判断
if (now->left == nullptr && now->right == nullptr) {
if (temp == sum) {
return true;
}
continue;
}
//左右节点入队,路径长度更新
if (now->left != nullptr) {
que_node.push(now->left);
que_val.push(now->left->val + temp);
}
if (now->right != nullptr) {
que_node.push(now->right);
que_val.push(now->right->val + temp);
}
}
return false;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/path-sum/solution/lu-jing-zong-he-by-leetcode-solution/