一、110. 平衡二叉树
状态:已解决
1.思路
这道题由于要求高度,因此不能从上往下遍历了,而是要从下往上遍历(高度的定义),否则会出错,因此,层序遍历不再适用。我们这里依旧选择后序遍历的做法。
此题的复杂点在于怎么保存结果,也就是递归函数返回值是什么更合适。如果返回的是子树的高度,那么即使发现了存在不平衡的子树也没有办法返回。如果返回的是对子树是否平衡的判断,那么子树的节点总数也没有办法保存。
解决这个难点有两种思路:
(1)返回值为数值,如果有子树不再平衡,就返回-1(也可以是任何正常平衡树不可能出现的深度值),并且每次遍历完一棵就立即判断返回值是否为-1,是的话就一直保持这个返回值返回下去,如果没有出现-1就返回节点总数。
(2)在参数列表里面设置一个变量,并传它的引用,让该变量保存是否出现过非平衡树的状态。
2.代码实现
(1)设置-1为标志的方法:
class Solution {
public:
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
};
(2)添加参数引用保存状态的方法:
class Solution {
public:
int getDepth(TreeNode* node,bool & judge){
if(node == NULL) return 0;
int leftNum=getDepth(node->left,judge);
if(judge == false) return 0;//judge为false时,说明出现左子树不相等了,此时直接返回
//不用再遍历右子树,相当于剪枝了一下
int rightNum=getDepth(node->right,judge);
if(abs(leftNum-rightNum)>1) judge=false;
if(judge == false) return 0;//judge为false时,说明出现右子树不相等了
return max(leftNum,rightNum)+1;
}
bool isBalanced(TreeNode* root) {
bool judge = true;
getDepth(root,judge);
return judge;
}
};
二、257.二叉树的所有路径
题目链接/文章讲解/视频讲解: https://programmercarl.com/0257.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%89%80%E6%9C%89%E8%B7%AF%E5%BE%84.html状态:已解决
1.思路
这道题是一道标准的回溯题模板,递归和回溯相辅相成,递归主要侧重于每个子过程的处理过程,而回溯主要侧重于利用从子过程回来父级过程中,工作栈进行的一些现场恢复的工作原理。比如,我们传一个形参到递归函数中,那么该参数从子过程回到父级过程时,值也会复原。这就很方便我们保存路径了,甚至于不需要我们专门将前一个路径节点从容器中pop出,例:
假如我们第一次从A到B的子过程时,用引用字符串保存了"A->B"的路径,后面回溯到A节点的处理过程时,就需要把B从字符串中pop出来,这样才能继续去实现A->C的路径保存。然而如果我们直接将该字符串作为形参传递 ,那么在回到A过程时,该参数会恢复到传递前的值,即形变实不变的原理。
另外,这道题由于要保存路径,因此只能从上往下遍历,也就是只能前序遍历。
2.代码实现
应用递归三部曲的公式:
(1)确定参数和返回值类型:
void traversal(TreeNode* node,string path,vector<string> & result)
这里已经有结果的引用参数了,因此无需返回东西。
(2) 确定终止条件:
if(node->left == NULL && node->right == NULL)
这里不再是为空才终止了,遍历到叶子节点就要结束,不然由前序遍历的原理空节点也要存入路径中了。
(3)确定单层递归逻辑:
因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。
path.push_back(node->val);
然后是递归和回溯的过程,上面说过没有判断node是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。所以递归前要加上判断语句。
完整代码为:
class Solution {
public:
void traversal(TreeNode* node,string path,vector<string> & result){
path += to_string(node->val);//中
if(node->left == NULL && node->right == NULL){
result.push_back(path);
return ;
}
if(node->left){
traversal(node->left,path+"->",result);
}//左
if(node->right){
traversal(node->right,path+"->",result);
}//右
return ;
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
if(root == NULL) return result;
traversal(root,"",result);
return result;
}
};
三、404.左叶子之和
状态:已解决
1.思路
我是用了一个标志来判断是否是左子树还是右子树的。如果遍历到一个叶子节点,并且标签显示为左子树遍历方式时,就将该节点的值加到结果中,很好理解,但是我不太能看出其遍历顺序。
老师的讲解为:判断一个节点的左子树是否为一个叶子节点,是的话,由于它是左遍历的叶子节点,那么它就一定是左叶子。采用的是后序遍历,还是因为是在中间结点统计左子树和右子树的左叶子之和。
2.代码实现
(1)我的版本:
class Solution {
public:
void LeftLeaves(TreeNode* node,int & result,int direction){
if(node->left == NULL && node->right == NULL && direction == 1){
result += node->val;
return;
}
if(node->left) LeftLeaves(node->left,result,1);
if(node->right) LeftLeaves(node->right,result,0);
}
int sumOfLeftLeaves(TreeNode* root) {
int result=0;
if(root == NULL) return result;
LeftLeaves(root,result,0);
return result;
}
};
(2)老师的版本
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL) return 0;
if (root->left == NULL && root->right== NULL) return 0;
int leftValue = sumOfLeftLeaves(root->left); // 左
int rightValue = sumOfLeftLeaves(root->right); // 右
if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况
leftValue = root->left->val;
}
int sum = leftValue + rightValue; // 中
return sum;
}
};