代码随想录算法训练营day17

题目:110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和

参考链接:代码随想录

110.平衡二叉树

思路:首先要搞清楚高度和深度的区别,深度是从上往下数,应该使用前序遍历的思路,而高度是从下往上数,只能后序遍历,力扣上的深度都是从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。

257. 二叉树的所有路径

思路:本题要从头结点开始遍历,肯定是前序遍历,只是当遇到空节点的时候,需要往前回溯,然后再往后遍历,这就是第一次真正使用回溯法。在具体代码中,将路径转换单独写一个函数。对于递归过程,目前遍历的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

404.左叶子之和

思路:本题就是简单的前序遍历,关键是判断左叶子,当遍历到某个节点时,如果其左孩子不为空且其为叶子节点,则说明是左叶子。

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。

你可能感兴趣的:(算法)