LC 110.平衡二叉树
题目链接:LC 110.平衡二叉树
思路:如何判断一个二叉树是否平衡?看节点是否都满足左右子树的高度差不超过2,若满足则为平衡二叉树,否则为不平衡二叉树,计算高度一般使用的是后序遍历。
本题的思路为:(递归三步)
函数参数和返回值:参数为节点地址,返回值为以输入节点为根节点的树的高度(必须要知道高度,否则无法判断是否平衡),若树不平衡则直接返回-1,无需返回树的高度。
终止(返回)条件:当节点遇到空节点返回0,表示树高为0。
单层递归逻辑:如何判断当前节点是否为平衡二叉树?判断该节点的左右子树高度的差值。若左右子树本身就不平衡,就直接不平衡。然后判断差是否大于等于2,若满足,则也不平衡;若平衡,就返回该节点的高度,也就是左右子树高度取大值加一。平衡就返回高度,不平衡就返回-1。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//输入是遍历到的节点
//输出是若平衡,节点为根节点的高度;若不平衡,则为-1
int getHeight(TreeNode* cur){
if(cur==nullptr)return 0;//为空节点则返回0
int leftHeight = getHeight(cur->left);
if(leftHeight==-1)return -1;
int rightHeight = getHeight(cur->right);
if(rightHeight==-1)return -1;
if(abs(rightHeight-leftHeight)>1)return -1;
return 1+max(rightHeight, leftHeight);
}
bool isBalanced(TreeNode* root) {
int result = getHeight(root);
if(result==-1)return false;
else return true;
}
};
LC 257. 二叉树的所有路径
题目链接:LC 257. 二叉树的所有路径
思路:需要保存从根节点到叶子节点的路径,因此使用前序遍历和回溯。回溯和递归是一家子,有递归必定有回溯。
递归三步:
递归参数和返回值:输入为遍历的节点地址和之前保存的结果,由于直接将结果保存,所以不用返回值
终止条件:当遇到叶子节点就终止,先保存叶子节点再终止,和之前不同,之前是遇到空节点再终止。
单层递归逻辑:先保存结果,然后分别进入左子树,右子树。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result){//第二个输入是,保存路径,当有一条完整路径(到叶子节点)就将结果放到result中,并且进行回溯(删除后面的节点)
path.push_back(cur->val);//中,因为到叶子节点就终止了,所以要把这部分写到终止条件前,为了让叶子节点也在path中,为了让回溯更优美,不然需要加入判断:当遇到叶子节点不能回溯
//终止条件(不严格)
//当节点为叶子节点时,就保存路径path
if(cur->left==nullptr && cur->right==nullptr){
string sPath;
//先保存之前的
for(int i=0; i<path.size()-1; i++){//注意不能用i<=path.size()-2,因为是无符号数-2就可能是无限大的数,可用int变量进行保存
sPath += to_string(path[i]);
sPath += "->";
}
//再保存叶子节点
sPath += to_string(path[path.size()-1]);
//将结果保存
result.push_back(sPath);
return;
}
//单层递归逻辑
if(cur->left){
traversal(cur->left, path, result);//当遇到叶子节点return后,需要把后面的节点弹出
path.pop_back();//回溯,每个递归都要有对应的回溯
}
if(cur->right){
traversal(cur->right, path, result);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if(root==nullptr)return result;
traversal(root, path, result);
return result;
}
};
LC 404.左叶子之和
题目链接:LC 404.左叶子之和
思路:左叶子之和,不可以先判断是否是叶子,再判断是否为左叶子,因为左叶子只能通过父节点判断,因此需要注意要判断节点是否有左子树,然后判断左子树是否是叶子节点,如果是左子树叶子节点那么就取叶子节点的值。
**为什么用后序遍历?**后序遍历递归参数值只需节点地址即可,前序遍历需要把左叶子值总和相加也传递到后面。
递归三部:
递归的参数和返回值:参数为节点地址,返回值为已知的左叶子节点值总和。
终止条件:当遍历到空节点返回的是0;当遍历到叶子节点返回的也是0;(?那我能不能只判断到叶子节点呢?)
单层递归逻辑:递归取左子树的左叶子之和,若左子树为叶子节点就取叶子节点的值;同样递归取右子树的左叶子之和,最终总和为左右子树的左叶子之和。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//方案1
int sumOfLeftLeaves(TreeNode* root) {
if(root->left==nullptr && root->right==nullptr)return 0;
int left=0;
int right=0;
//处理左子树
if(root->left)left = sumOfLeftLeaves(root->left);//作用是对左子树进行递归遍历,返回值都为0,没什么实际意义
//返回之后对叶子节点的父节点进行判断
if(root->left!=nullptr && root->left->left==nullptr && root->left->right==nullptr && root)left = root->left->val;
//处理右子树
if(root->right)right = sumOfLeftLeaves(root->right);
//左右子树的左叶子节点之和
int sum = right + left;
return sum;
}
//方案2
int dfs(TreeNode* cur, bool isLeft){
//当遇到空节点返回0;
if(cur==nullptr)return 0;
//不为空节点时,就判断是否是叶子节点
int left = dfs(cur->left, true);
int right = dfs(cur->right, false);
if(cur->left==nullptr && cur->right==nullptr && isLeft){
left = cur->val;
}
return left+right;
}
int sumOfLeftLeaves(TreeNode* root) {
int result = dfs(root, false);
return result;
}
};
总结
通过这几天的实践,可以看出后序遍历在计算二叉树深度和节点高度时非常方便,前序遍历反而要考虑很多东西。当在统计二叉树的路径时,使用前序遍历比较合适。