代码随想录算法训练营第十七天|LeetCode110.平衡二叉树、LeetCode257.二叉树的所有路径、LeetCode404.左叶子之和

本文介绍了如何解决LeetCode中的三道二叉树问题:110.平衡二叉树、257.二叉树的所有路径和404.左叶子之和。分别使用后序遍历、回溯等方法,探讨了不同解题策略,并分析了时间复杂度和空间复杂度。

代码随想录算法训练营第十七天|LeetCode110.平衡二叉树、LeetCode257.二叉树的所有路径、LeetCode404.左叶子之和

2023年3月16日 第三十天补

110. 平衡二叉树

题目链接:110. 平衡二叉树

思路:

  • 一个平衡二叉树,左右两侧子树的高度差不大于一
  • 这个遍历我们需要用后序遍历,因为只有知道左右子树的高速,才能判断当前节点的树是否是平衡二叉树
  • 另外,如果有子树不是平衡二叉树,则整个树都不是平衡二叉树

时间复杂度:O(n),空间复杂度O(n)

代码:

class Solution {
public:
    bool isBalanced(TreeNode* root) {
        // 只有-1表示该树不是平衡二叉树
        if(getHeight(root) != -1)
        return ture;
        else return false;
        
    }

    // 获取子树高度的函数
    int getHeight(TreeNode*node)
    {   
        if(node == nullptr)return 0;
        // 获取左右子树的高度
        auto left = getHeight(node->left);
        auto right = getHeight(node->right);
        // 用-1代表不是平衡二叉树
        // 如果子树或当前树不是平衡二叉树,则返回-1
        if(left == -1 || right == -1 || std::abs(right - left) > 1)return -1;
        // 否则返回当前节点的高度
        else return std::max(right,left)+1;
    }
};

总结:

  • 因为函数的返回值既要有高度又要有是否是平衡二叉树的判断,我原来在这里定义了一个结构体
  • 听了carl哥的讲解之后理解到,如果不是平衡二叉树,高度就没有意义了,那么就找一个高度不存在的值作为判断值,比如-1返回就行
  • 做题体验:嘿嘿哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷

257. 二叉树的所有路径

题目链接:257. 二叉树的所有路径

思路:

  • 这道题其实也是一个遍历找叶子节点的过程,遍历的时候要记录下所有经过的节点
  • 找到叶子节点就将路径放入结果集中
  • 这道题我写三个题解
  • 解法1:用代码比较精简的写法
  • 解法2:在代码逻辑中考虑到回溯
  • 解法3:看carl哥视频优化解法2

时间复杂度:O(n),空间复杂度O(n)

解法1

代码:

class Solution
{
public:
    vector<string> binaryTreePaths(TreeNode *root)
    {
        std::string path;
        if (root != nullptr)travelNode(root, path);
        return result;
    }
    void travelNode(TreeNode *node, std::string path)
    {
        // 在路径上加上当前元素
        path = path + std::to_string(node->val) + "->";
        // 如果是叶子节点,就放入路径的结果集,记得要删掉最后的"->"
        if (node->left == nullptr && node->right == nullptr)result.push_back(path.substr(0, path.size() - 2));
        // 如果还有孩子就递归
        if (node->left != nullptr)travelNode(node->left, path);
        if (node->right != nullptr)travelNode(node->right, path);
    }
    std::vector<string> result;
};

解法2

  • 上一版代码每递归一次,都会拷贝一个新的string对象出来,会比较浪费空间
  • 这一版在函数的传参是用引用
  • 所以要注意的是,递归在回溯的过程要删除前一个节点的记录

代码:

class Solution
{
public:
    vector<string> binaryTreePaths(TreeNode *root)
    {
        std::string path;
        if (root != nullptr)
        {
            travelNode(root, path);
        }
        return result;
    }
    // 是用回溯的原因,是因为参数用到了引用
    void travelNode(TreeNode *node, std::string &path)
    {

        path = path + std::to_string(node->val) + "->";
        if (node->left == nullptr && node->right == nullptr)
        {
            result.push_back(path.substr(0, path.size() - 2));
            return;
        }
        if (node->left != nullptr)
        {
            travelNode(node->left, path);
            // 注意要删的是刚遍历过的节点,不是当前节点
            // std::to_string(node->left->val).size() 代表刚遍历过的节点写入路径的长度
            // 2 代表->的长度
            auto valLength = std::to_string(node->left->val).size();
            path = path.substr(0, path.size() - valLength - 2);
        }
        if (node->right != nullptr)
        {
            travelNode(node->right, path);
            auto valLength = std::to_string(node->right->val).size();
            path = path.substr(0, path.size() - valLength - 2);
        }
    }
    std::vector<string> result;
};

解法3

  • 上一版代码有此问题:
  • 每次添加路径的时候都要从int型转换成string,逻辑上不容易理解
  • 每次回溯删值得时候要注意长度,注意删的是哪个节点的值
  • 这版代码就用一个std::vector 对象作为形参
  • 每次添加路径只要尾插当前元素的值即可
  • 删除路径只要抛出尾部元素
  • 然后需要再写一个生成string对象的函数,让代码整体显得更加整洁一些

代码:

class Solution
{
public:
    vector<string> binaryTreePaths(TreeNode *root)
    {
        std::vector<int> path;
        std::vector<string> result;
        if (root != nullptr)
        {
            travelNode(root, path,result);
        }
        return result;
    }
    void travelNode(TreeNode *node, std::vector<int> &path,std::vector<string> & result)
    {

        path.push_back(node->val);
        if (node->left == nullptr && node->right == nullptr)
        {
            result.push_back(genString(path));
            return;
        }
        if (node->left != nullptr)
        {
            travelNode(node->left, path,result);
            path.pop_back();
        }
        if (node->right != nullptr)
        {
            travelNode(node->right, path,result);
            path.pop_back();
        }
    }

    std::string genString(std::vector<int> path)
    {
        std::string res;
        if(!path.empty())
        {
            res += std::to_string(path[0]);
        }
        for(int i  =1;i<path.size();++i)
        {
            res +="->";
            res +=std::to_string(path[i]);
        }
        return res;
    }
    
};

总结:

  • 理解起来不是太难,加油吧
  • 做题体验:哈哈哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷

404. 左叶子之和

题目链接:404. 左叶子之和

思路:

  • 我认为这道题的关键,是找到左叶子的父亲

时间复杂度:O(n),空间复杂度O(n)

代码:

class Solution
{
public:
    int sumOfLeftLeaves(TreeNode *root)
    {
        getLeftNode(root);
        return sum;
    }
    void getLeftNode(TreeNode *node)
    {
        // 判断是否是左叶子的父亲
        if (node->left != nullptr && node->left->left == nullptr && node->left->right == nullptr)
            sum += node->left->val;
        if (node->left != nullptr)
            getLeftNode(node->left);
        if (node->right != nullptr)
            getLeftNode(node->right);
    }
    int sum;
};

总结:

  • 理解起来不是太难,加油吧
  • 做题体验:哈哈哈哈 > 嘿嘿 > 哎哎 > 嘤嘤
  • 时间:未计
  • 一刷
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值