代码随想录算法训练营第17天 | 第六章 二叉树 part04 ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

本文介绍了三个关于二叉树的编程问题:110.平衡二叉树,257.二叉树的所有路径和404.左叶子之和。强调了递归和回溯的方法,分别提供了递归和迭代的解决方案,并讨论了树的深度、高度计算以及如何识别左叶子节点。
摘要由CSDN通过智能技术生成

第六章 二叉树part04

今日内容:

●  110.平衡二叉树

●  257. 二叉树的所有路径

●  404.左叶子之和


详细布置

迭代法,大家可以直接过,二刷有精力的时候 再去掌握迭代法。

110.平衡二叉树 (优先掌握递归)

【链接】(文章,视频,题目)

再一次涉及到,什么是高度,什么是深度,可以巩固一下。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】

【看后想法】

  • 深度与高度

    • 深度,是从根节点

    • 高度,是到叶子结点

    • 联系:树的深度=树的高度=根节点的最大高度

  • 要获取左右子树的高度,高度,后序遍历左右中

  • 用-1高度做特殊标志位,表示子树已经不是高度平衡

  • 迭代方法效率较低,不掌握

【实现困难】

【自写代码】

class Solution {
public:
    // 后序遍历左右中,求高度
    // 返回值应为高度,-1表示已经不是高度平衡
    int GetHeight(TreeNode* node) {
        // 结束条件
        if (!node) {
            return 0;
        }
        int left_height = GetHeight(node->right);
        if (left_height == -1) {
            return -1;
        }
        int right_height = GetHeight(node->left);
        if (right_height == -1) {
            return -1;
        }
        if (std::abs(left_height - right_height) > 1) {
            return -1;
        }
        return 1 + std::max(left_height, right_height);
    }
    bool isBalanced(TreeNode* root) {
        return GetHeight(root) == -1 ? false : true;
    }
};

【收获与时长】45m


257. 二叉树的所有路径 (优先掌握递归)

【链接】(文章,视频,题目)

这是大家第一次接触到回溯的过程, 我在视频里重点讲解了 本题为什么要有回溯,已经回溯的过程。

如果对回溯 似懂非懂,没关系, 可以先有个印象。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】

  • 找出所有的叶子结点,连续回溯找到父亲的父亲,直到根节点

  • 没想好用什么结构去存储父节点信息

【看后想法】

  • 由于要从根节点到叶子结点的路径,因此选择前序遍历遍历,中左右

  • 递归结束条件,找到叶子结点,需要将跟到叶子的路径存下来,因此考虑每层递归中,把从根节点到当前结点的路径作为一个参数,并且把需要返回的多个路径集合作为另一个参数。函数类型void即可

【实现困难】

【自写代码】

class Solution {
public:
    void traversal(TreeNode* node, std::vector<int>* path_ptr, std::vector<std::string>* path_str_vec_ptr) {
        // 中,先处理,因为叶子结点也要接入到路径中
        path_ptr->emplace_back(node->val);
        // 结束条件,自身非空,且叶子结点
        // 注意不允许空结点调用traversal函数,在调用之前先判断树结点指针有效
        if (node && !node->left && !node->right) {
            std::string path_str;
            if (!path_ptr->empty()) {
                path_str += std::to_string(path_ptr->front());
            }
            for (int i = 1; i < path_ptr->size(); ++i) {
                path_str += "->";
                path_str += std::to_string(path_ptr->at(i));
            }
            path_str_vec_ptr->emplace_back(std::move(path_str));
        }
        // 单层逻辑,前序遍历中左右
        if (node->left) {
            traversal(node->left, path_ptr, path_str_vec_ptr);
            path_ptr->pop_back(); // 回溯,遍历完左子树,要剔除左孩子
        }
        if (node->right) {
            traversal(node->right, path_ptr, path_str_vec_ptr);
            path_ptr->pop_back(); // 回溯,遍历完右子树,要剔除右孩子
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        std::vector<int> path;
        std::vector<std::string> res;
        if (!root) {
            return res;
        }
        traversal(root, &path, &res);
        return res;
    }
};
class Solution {
public:
    // 递归回溯,前序遍历,隐含回溯,C++语法简化
    void TraversePath(TreeNode* node, std::string path, std::vector<std::string>* res_ptr) {
        if (!node) {
            std::cout << "empty node" << std::endl;
            return;
        }
        // 注意路径字符串,是拷贝传参,下层的修改不影响上层原本的参数
        // 中,对根节点做特殊处理
        if (path.empty()) {
            path += std::to_string(node->val);
        } else {
            path += "->";
            path += std::to_string(node->val);
        }
        // 结束条件
        if (!node->left && !node->right) {
            res_ptr->emplace_back(path);
        }
        // 前序遍历处理单层逻辑,中左右
        if (node->left) {
            // 此处不需要回溯,因为path不会因为下一层的递归而改变
            TraversePath(node->left, path, res_ptr);
        }
        if (node->right) {
            TraversePath(node->right, path, res_ptr);
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        std::string path;
        vector<string> res;
        if (!root) {
            return res;
        }
        TraversePath(root, path, &res);
        return res;
    }
};

迭代方法

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        // 迭代方法,前序遍历,同时维护两个栈,树栈和路径栈,树栈每一个结点,同时对应路径栈的一个元素,也即该结点的完整路径(从根结点)
        std::vector<std::string> res;
        std::stack<TreeNode*> tree_st;
        std::stack<std::string> path_st;
        if (root) {
            tree_st.push(root);
            path_st.push(std::to_string(root->val));
        }
        while (!tree_st.empty()) {
            TreeNode* node = tree_st.top();
            tree_st.pop();
            std::string path = path_st.top();
            path_st.pop();
            // 中,处理,遇到叶子结点
            if (!node->left && !node->right) {
                res.emplace_back(path);
            }
            // 右左,访问
            if (node->right) {
                tree_st.push(node->right);
                path_st.push(path + "->" + std::to_string(node->right->val));
            }
            if (node->left) {
                tree_st.push(node->left);
                path_st.push(path + "->" + std::to_string(node->left->val));
            }
        }
        return res;
    }
};

【收获与时长】1h


404.左叶子之和 (优先掌握递归)

【链接】(文章,视频,题目)

其实本题有点文字游戏,搞清楚什么是左叶子,剩下的就是二叉树的基本操作。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】

  • 递归方式解决,单层逻辑就是:左叶子和 = 自身结点有无左右叶子 + 左子树 + 右子树

【看后想法】

  • 简化递归,合并函数,究竟是什么顺序感觉不太重要,可以说是前中后都行

  • 注意,左叶子的定义,就决定了必须从父结点来判断,不能从自身来判断。左叶子=是左孩子,并且是叶子结点

【实现困难】

【自写代码】

class Solution {
public:
    // 递归,简化代码,合并函数
    int sumOfLeftLeaves(TreeNode* root) {
        // 结束条件
        if (!root) {
            return 0;
        }
        int res = 0;
        // 判断自身的左孩子是叶子
        if (root->left && !root->left->left && !root->left->right) {
            res += root->left->val;
        }
        res += sumOfLeftLeaves(root->left);
        res += sumOfLeftLeaves(root->right);
        return res;
    }
};
class Solution {
public:
    // 迭代方法,遍历过程中,每个节点判断自身的左孩子,是否左叶子
    int sumOfLeftLeaves(TreeNode* root) {
        int res = 0;
        // 前序遍历,中左右,入栈右左,非空才入栈
        std::stack<TreeNode*> st;
        if (root) {
            st.push(root);
        }
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            // 中,判断自身的左孩子是不是叶子
            if (node->left && !node->left->left && !node->left->right) {
                res += node->left->val;
            }
            // 右
            if (node->right) {
                st.push(node->right);
            }
            // 左
            if (node->left) {
                st.push(node->left);
            }
        }
        return res;
    }
};

【收获与时长】1h

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值