参考链接:代码随想录
思路:首先要搞清楚高度和深度的区别,深度是从上往下数,应该使用前序遍历的思路,而高度是从下往上数,只能后序遍历,力扣上的深度都是从1开始的。本题首先写一个计算高度的函数,然后对比左右子树的高度即可。对于求高度的函数,之前求最大高度已经写过,此处的区别就是当左右不平衡的时候直接返回-1。时间复杂度O(n),只要是遍历都是O(n)。
class Solution {
public:
int getheight(TreeNode* root){//求某个节点的高度,如果为-1,表示不是平衡二叉树
if(!root){
return 0;
}
int leftHeight=getheight(root->left);
if(leftHeight==-1){//只要有一个子树不是平衡的,那整个二叉树就不是平衡的
return -1;
}
int rightHeight=getheight(root->right);
if(rightHeight==-1){
return -1;
}
if(abs(leftHeight-rightHeight)>1){//返回-1的情况,左右高度绝对值大于1
return -1;
}
else{
return 1+max(leftHeight,rightHeight);
}
}
bool isBalanced(TreeNode* root) {
if(getheight(root)==-1){
return false;
}
return true;
}
};
迭代法pass。
思路:本题要从头结点开始遍历,肯定是前序遍历,只是当遇到空节点的时候,需要往前回溯,然后再往后遍历,这就是第一次真正使用回溯法。在具体代码中,将路径转换单独写一个函数。对于递归过程,目前遍历的path和最终结果ans都是使用引用传递供全局使用。首先是返回条件,当递归达到叶子节点就需要返回,不能到达空节点,故到达叶子节点就可以增加新答案。然后是递归过程,处理完非叶子节点,然后分别处理左右非空节点,处理完一个节点将path的最后一项删除,即当前处理的节点,这就是回溯的过程。
public:
string convertPath(vector<int>& path){//将vector转换为string
string sPath;
int i;
for(i=0;i<path.size()-1;i++){
sPath+=to_string(path[i]);
sPath+="->";
}
sPath+=to_string(path[i]);//最后一个
return sPath;
}
void getPath(TreeNode* root,vector<int>& path,vector<string>& ans){//默认root不为空,才有路径
//path使用int记录路径,ans为保存的结果
path.push_back(root->val);//因为进入这个函数肯定不为空,直接加入path
if(!root->left&&!root->right){//两边孩子都为空,说明到达路径末
string sPath=convertPath(path);//到达末尾,直接将记录的路径转换为字符串
ans.push_back(sPath);
return;
}
if(root->left){
getPath(root->left,path,ans);
path.pop_back();//回溯,删除最后一个数据
}
if(root->right){
getPath(root->right,path,ans);
path.pop_back();
}
return;
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
if(!root){//根节点为空
return ans;
}
vector<int> path;//一开始路径为空,path会在计算中不断回溯,因为传递的引用
getPath(root,path,ans);
return ans;
}
};
看完解析发现代码可以简化,即将path使用string形式,在遍历过程中就完成string的计算,省去了转换的步骤:
class Solution {
public:
void getPath(TreeNode* root,string path,vector<string>& ans){//默认root不为空,才有路径
path+=to_string(root->val);//到达某个非空节点就增加路径
if(!root->left&&!root->right){//两边孩子都为空,说明到达路径末
ans.push_back(path);
return;
}
if(root->left){
getPath(root->left,path+"->",ans);//加上箭头
//这里不用再回溯,因为path不是引用,后面还是使用的之前的path
}
if(root->right){
getPath(root->right,path+"->",ans);
}
return;
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
if(!root){//根节点为空
return ans;
}
string path;
getPath(root,path,ans);
return ans;
}
};
使用vector的写法也可以不用引用传递,减少了pop_back的步骤,在实际递归完成后path自动变成原来的值,完成回溯:
class Solution {
public:
string convertPath(vector<int>& path){//将vector转换为string
string sPath;
int i;
for(i=0;i<path.size()-1;i++){
sPath+=to_string(path[i]);
sPath+="->";
}
sPath+=to_string(path[i]);//最后一个
return sPath;
}
void getPath(TreeNode* root,vector<int> path,vector<string>& ans){//默认root不为空,才有路径
//path使用int记录路径,ans为保存的结果
path.push_back(root->val);//因为进入这个函数肯定不为空,直接加入path
if(!root->left&&!root->right){//两边孩子都为空,说明到达路径末
string sPath=convertPath(path);//到达末尾,直接将记录的路径转换为字符串
ans.push_back(sPath);
return;
}
if(root->left){
getPath(root->left,path,ans);
}
if(root->right){
getPath(root->right,path,ans);
}
return;
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
if(!root){//根节点为空
return ans;
}
vector<int> path;//一开始路径为空,path会在计算中不断回溯,因为传递的引用
getPath(root,path,ans);
return ans;
}
};
迭代法pass
思路:本题就是简单的前序遍历,关键是判断左叶子,当遍历到某个节点时,如果其左孩子不为空且其为叶子节点,则说明是左叶子。
class Solution {
public:
void getLeftSum(TreeNode* root,int& ans){//进入递归说明不是空节点
if(!root){//返回
return;
}
if(root->left&&!root->left->left&&!root->left->right){//判断节点的左孩子为叶子
ans+=root->left->val;
}
getLeftSum(root->left,ans);//前序遍历
getLeftSum(root->right,ans);
}
int sumOfLeftLeaves(TreeNode* root) {
int ans=0;
getLeftSum(root,ans);
return ans;
}
};
标答没有使用引用传递,而是直接计算,和计算深度的方法差不多:
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if(!root){//返回
return 0;
}
int ans=0;
if(root->left&&!root->left->left&&!root->left->right){//判断节点的左孩子为叶子
ans+=root->left->val;
}
ans+=sumOfLeftLeaves(root->left);
ans+=sumOfLeftLeaves(root->right);
return ans;
}
};
迭代法pass。