算法day14|110.平衡二叉树 (优先掌握递归)、 二叉树的所有路径(优先掌握递归)、404.左叶子之和 (优先掌握递归)、222.完全二叉树的节点个数(优先掌握递归)
110.平衡二叉树 (优先掌握递归)
我用我原来的思路去做,发现运行不了,目前也没有找到问题…
class Solution {
public:
bool travel(TreeNode*root,int &count)
{
if(root==nullptr)
return false;
count++;
int count1=count,count2=count;
travel(root->left,count1);
travel(root->right,count2);
if(count1-count2>1||count2-count1>1)
return false;
count=count1>count2?count1:count2;
return true;
}
bool isBalanced(TreeNode* root) {
int count=0;
if(root!=nullptr)
return travel(root,count);
}
};
因为求深度:从上到下去查 所以需要前序遍历,而高度:从下到上去查,所以只能后序遍历
但是104.二叉树的最大深度 (opens new window)用的是后序遍历。那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。
正确的代码:
class Solution {
public:
int getHeight(TreeNode*root)
{
if(root==nullptr)
return 0;
int left=getHeight(root->left);
if(left==-1) return -1;
int right=getHeight(root->right);
if(right==-1) return -1;
int result;
if(abs(left-right)>1)
result= -1;
else
result=1+(left>right?left:right);
return result;
}
bool isBalanced(TreeNode* root) {
if(root==nullptr)
return true;
if(getHeight(root)==-1)
return false;
else
return true;
}
};
总体思路(递归):
- 返回值为int,原因是我们需要知道每个结点的高度,用int就不需要引入count了,让代码更加清晰
- 终止条件(2个):
- null,返回0;
- 根据题目要求,发现不符合平衡二叉树的要求,返回-1;
- 单层递归:由于我们需要先知道孩子的信息(即高度),我们才能确定当前节点的高度。所以我们使用后序遍历。当获取孩子的信息之后,如果abs>1,要返回-1;反之,说明在结点的统治下全部都是平衡二叉树。那么需要返回这个结点的高度,即左右孩子中的最大高度+1;
需要注意的是,当出现一个结点不满足平衡二叉树时,它上面的结点就都不满足了。所以在后序遍历的时候,如果孩子节点返回的时-1,那么父节点也要直接返回-1;
代码细节:
- 绝对值函数abs(),最大值函数max()
- 空树也是平衡二叉树
- 变量在定义的时候不一定要赋值,在使用前被赋值即可。所以可以直接写int result,后面没有去使用result,只有对result的赋值。
- 注意运算的优先级:result=1+(left>right?left:right),这里的三元运算符要加括号或者直接使用max()
257. 二叉树的所有路径(优先掌握递归)
class Solution {
public:
void traversal(TreeNode*root,vector<int>&path,vector<string>& result)
{
path.push_back(root->val);
if(root->left==nullptr&&root->right==nullptr)
{
string str;
for(int i=0;i<path.size()-1;i++)
{
str+=to_string(path[i])+"->";
}
str+=to_string(path[path.size()-1]);
result.push_back(str);
return;
}
if(root->left)
{
traversal(root->left,path,result);
path.pop_back();
}
if(root->right)
{
traversal(root->right,path,result);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> result;
if(root!=nullptr)
traversal(root,path,result);
return result;
}
};
总体思路(递归):
-
参数:vector result是最后需要的结果,而vector是作为缓冲的,先把这些值放到整数型数组里面,然后放完之后统一转化成字符串型数组。
-
**终止条件:**这道题比较特殊,它是在当该节点为叶子结点的时候终止。终止的时候,这条路径的整数型数组已经放满了。它会完成一个非常重要的操作:把整数型数组转化成字符型数组。这个我愿意定义为终止条件下的特殊行为(这些行为只有在终止的时候才会执行)
-
单层递归:
- 为了最容易地形成这样的路径(由父—>孩子这样的顺序),显然是采用先序遍历,先存放父,再存放孩子,这样在整数型数组里的顺序也是和字符串性别数组的顺序是一致的。
- 对于当前结点,我们只需要将结点的值存入整数型数组即可。为了和叶节点的逻辑统一,我们把这个操作放在终止条件前面。
- 对于孩子结点,为了避免空指针异常的问题(在叶子节点的时候可能会发生),在递归之前需要判断该节点是否为空。还有最为重要的是,在左孩子的递归结束后,要将整数型数组里面的元素抛出一个,这就是大名鼎鼎的回溯.
注意,不是说左孩子刚把自己放入数组就立刻抛出,那不放个寂寞…,所以正确的理解是:当左孩子的整个递归全部执行完之后,也就是在它自己的递归里面还要向下递归呢,直到无法递归为止,这才算结束。所以这里的左孩子的含义就是左子树,当当前结点的左子树全部递归完、回溯完了之后,才轮到当前结点的左孩子作回溯。
代码细节:
- to_string(整数型)函数,可将整数型转化字符串,最后返回的字符串形式
- 终止条件下的return 千万不能忘了,否则递归就没有出口了,结束不了了
404.左叶子之和 (优先掌握递归)
掌握了上面的题目之后,这道题就小菜一碟了,具体代码如下:
class Solution {
public:
void traversal(TreeNode*root,vector<int>& vec,bool flag)
{
if(root->left==nullptr&&root->right==nullptr&&flag==true){
vec.push_back(root->val);
return;
}
if(root->left)
{
flag=true;
traversal(root->left,vec,flag);
}
if(root->right)
{
flag=false;
traversal(root->right,vec,flag);
}
}
int sumOfLeftLeaves(TreeNode* root) {
vector<int> vec;
bool flag=false;
if(root!=nullptr)
traversal(root,vec,flag);
int count=0;
for(int i=0;i<vec.size();i++)
{
count+=vec[i];
}
return count;
}
};
总体思路:同样以叶节点为终止条件,终止行为是将该左叶结点的值存入vector,非常简单。
222.完全二叉树的节点个数(优先掌握递归)
这题依旧小菜一碟,具体代码如下:
class Solution {
public:
int traversal(TreeNode*root)
{
if(root==nullptr)
return 0;
int left=traversal(root->left);
int right=traversal(root->right);
int result=1+left+right;
return result;
}
int countNodes(TreeNode* root) {
if(root==nullptr)
return 0;
else
{
return traversal(root);
}
}
};
总体思路:后序遍历,因为需要先利用孩子节点的信息。可以理解为从下往上一个一个加。