代码随想录算法训练营第18天 | 第六章 二叉树 part05 ● 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树 105.

2023年4月12日 22:33:54

第六章 二叉树 part05

今日内容

● 513.找树左下角的值

● 112. 路径总和 113.路径总和ii

● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

详细布置


找树左下角的值

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

本地递归偏难,反而迭代简单属于模板题, 两种方法掌握一下

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

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

  • 层序遍历模板题,比较简单

【看后想法】

  • 如果要用递归,反而比较复杂。递归遍历求深度,只要左比右先,任意顺序均可

【实现困难】

【自写代码】

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        // 层序遍历,最后一层,最左边
        std::queue<TreeNode*> que;
        if (root) {
            que.push(root);
        }
        int res = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode* node = que.front();
                que.pop();
                // 处理
                if (i == 0) {
                    res = node->val;
                }
                // 访问
                if (node->left) {
                    que.push(node->left);
                }
                if (node->right) {
                    que.push(node->right);
                }
            }
        }
        return res;
    }
};
class Solution {
public:
    int max_depth_;
    int bottom_left_value_;
    void Traverse(TreeNode* node, int depth) {
        // 结束条件
        if (!node) {
            return;
        }
        // 处理,判断是否新的最深层
        if (depth > max_depth_) {
            max_depth_ = depth;
            bottom_left_value_ = node->val;
        }
        // 任意遍历,左,右
        if (node->left) {
            Traverse(node->left, depth + 1);
        }
        if (node->right) {
            Traverse(node->right, depth + 1);
        }
    }
    int findBottomLeftValue(TreeNode* root) {
        // 递归遍历求深度,只要左比右先,任意顺序均可
        max_depth_ = INT_MIN;
        bottom_left_value_ = 0;
        Traverse(root, 1);
        return bottom_left_value_;
    }
};

【收获与时长】30m


路径总和

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

本题 又一次设计要回溯的过程,而且回溯的过程隐藏的还挺深,建议先看视频来理解

  1. 路径总和,和 113. 路径总和ii 一起做了。 优先掌握递归法。

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

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

  • 递归,前序遍历到叶子结点,记录当前和

【看后想法】

  • 对代码实现做了优化

    • 对于求和得到目标的题目,可以只把剩余还缺少的和作为一个参数,而不需要同时传递目标和与当前和

    • 本题属于不必遍历整颗二叉树的题目,如果有了任意一个路径综合符合要求,就应该立刻返回,因此必须有返回值,true立刻返回

  • 迭代方法,关键是到了叶子结点的时候要知道这条路径的和是多少,因此,每个节点在遍历过程中,要把对应的当前路径和存下来,一起放到栈中

  • 只需要搜索一条符合条件的路径,需要返回值来做早返回(一有符合条件的就返回)

【实现困难】

【自写代码】

class Solution {
public:
    // 递归简化
    bool IsHavePathSum(TreeNode* node, int targetSum) {
        // 递归只接受非空结点
        // 结束条件,叶子结点
        if (!node->left && !node->right) {
            return targetSum == node->val;
        }
        // 单层逻辑,左或右子树能拼成剩余目标和
        if (node->left && IsHavePathSum(node->left, targetSum - node->val)) {
            return true;
        }
        if (node->right && IsHavePathSum(node->right, targetSum - node->val)) {
            return true;
        }
        return false;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (!root) {
            return false;
        }
        return IsHavePathSum(root, targetSum);
    }
};
class Solution {
public:
    // 递归简化,复用题目函数
    bool hasPathSum(TreeNode* root, int targetSum) {
        // 根节点是空的特殊处理,或者认为是空节点的结束条件
        if (!root) {
            return false;
        }
        // 结束条件,叶子结点
        if (!root->left && !root->right) {
            return targetSum == root->val;
        }
        // 单层逻辑,左或右子树能拼成剩余目标和
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        // 迭代方法,栈中同时维护结点、当前路径和
        std::stack<std::pair<TreeNode*, int>> st;
        if (root) {
            st.push(std::pair<TreeNode*, int>(root, root->val));
        }
        while (!st.empty()) {
            auto cur_pair = st.top();
            st.pop();
            TreeNode* node = cur_pair.first;
            int cur_sum = cur_pair.second;
            // 处理
            if (!node->left && !node->right && cur_sum == targetSum) {
                return true;
            }
            // 访问,压栈右左
            if (node->right) {
                st.push(std::pair<TreeNode*, int>(node->right, cur_sum + node->right->val));
            }
            if (node->left) {
                st.push(std::pair<TreeNode*, int>(node->left, cur_sum + node->left->val));
            }
        }
        return false;
    }
};

【收获与时长】50m


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

113. 路径总和 II

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

  • 记录剩余和,当前路径,返回可用路径集合

【看后想法】

  • 由于是遍历所有路径,函数就不需要返回值了,而是在参数中多一个回传参数(出参)

  • 传入传出参数比较多的时候,可以考虑在类内设置一个成员变量,则函数接口会变得更简洁, 代价是随时有被类内其他函数修改的风险、可读性变差(?)

【实现困难】

【自写代码】

class Solution {
public:
    // 剩余和,路径,都已经考虑了node结点
    void FindAllPathsWithSum(TreeNode* node, int remainder_sum, vector<int>* path_ptr, vector<vector<int>>* res_ptr) {
        // 结束条件,结点默认非空
        if (!node->left && !node->right) {
            if (remainder_sum == 0) {
                res_ptr->emplace_back(*path_ptr);
            }
            return;
        }
        // 单层逻辑
        if (node->left) {
            path_ptr->emplace_back(node->left->val);
            FindAllPathsWithSum(node->left, remainder_sum - node->left->val, path_ptr, res_ptr);
            path_ptr->pop_back();
        }
        if (node->right) {
            path_ptr->emplace_back(node->right->val);
            FindAllPathsWithSum(node->right, remainder_sum - node->right->val, path_ptr, res_ptr);
            path_ptr->pop_back();
        }
    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> res;
        if (!root) {
            return res;
        }
        vector<int> path(1, root->val);
        FindAllPathsWithSum(root, targetSum - root->val, &path, &res);
        return res;
    }
};

【收获与时长】30m


从中序与后序遍历序列构造二叉树

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

本题算是比较难的二叉树题目了,大家先看视频来理解。

106.从中序与后序遍历序列构造二叉树,105.从前序与中序遍历序列构造二叉树 一起做,思路一样的

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

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

【看后想法】

  • 结束条件,遍历容器空

  • 找中间结点,后序遍历最后一个结点

  • 如果根节点有孩子,需要切割两种遍历

  • 寻找根节点下标,中序遍历

  • 切割中序,左闭右开

  • 切割后序,左闭右开

  • 递归获得左右孩子

【实现困难】

  • 语法问题

    • new动态分配,返回指针,new后面不需要星号,TreeNode* root = new TreeNode(root_val);

    • 用std::find()函数之后的迭代器判断,是不等于end(),而不是begin(),if (iter != inorder.end())

    • 用vec.begin() + index的方法来初始化vector,是左闭右开的

【自写代码】

class Solution {
public:
    TreeNode* BuildTreeFromInOrderPoserOrder(vector<int>& inorder, vector<int>& postorder) {
        // 结束条件,遍历容器空
        if (postorder.empty()) {
            return nullptr;
        }
        // 找根结点,后序遍历最后一个结点
        int root_val = postorder.back();
        TreeNode* root = new TreeNode(root_val);
        // 如果根节点有孩子,需要切割两种遍历
        if (postorder.size() == 1) {
            return root;
        }
        // 寻找根节点下标,中序遍历
        auto iter = std::find(inorder.begin(), inorder.end(), root_val);
        int root_idx = -1;
        if (iter != inorder.end()) {
            root_idx = std::distance(inorder.begin(), iter);
        } else {
            std::cout << "Can not find root_val:" << root_val << std::endl;
            return nullptr;
        }
        // 切割中序,左闭右开
        vector<int> inorder_left(inorder.begin(), inorder.begin() + root_idx);
        vector<int> inorder_right(inorder.begin() + root_idx + 1, inorder.end());
        // 切割后序,左闭右开
        postorder.resize(postorder.size() - 1);
        vector<int> postorder_left(postorder.begin(), postorder.begin() + root_idx);
        vector<int> postorder_right(postorder.begin() + root_idx, postorder.end());
        // 输出必要的调试信息

        // 递归获得左右孩子
        root->left = BuildTreeFromInOrderPoserOrder(inorder_left, postorder_left);
        root->right = BuildTreeFromInOrderPoserOrder(inorder_right, postorder_right);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.empty() || postorder.empty() || inorder.size() != postorder.size()) {
            return nullptr;
        }
        return BuildTreeFromInOrderPoserOrder(inorder, postorder);
    }
};
class Solution {
public:
    TreeNode* BuildTreeFromInorderPostorder(const vector<int>& inorder, int inorder_begin, int inorder_end, const vector<int>& postorder, int postorder_begin, int postorder_end) {
        // 结束条件,空
        if (postorder_begin >= postorder_end) {
            return nullptr;
        }
        // 取根节点,后序遍历最后一个
        int root_val = postorder[postorder_end - 1];
        TreeNode* root = new TreeNode(root_val);
        // 判断有叶子
        if (postorder_end - postorder_begin == 1) {
            return root;
        }
        // 根节点中序下标
        int root_idx = -1;
        for (int i = inorder_begin; i < inorder_end; ++i) {
            if (inorder[i] == root_val) {
                root_idx = i;
                break;
            }
        }
        if (root_idx == -1) {
            std::cout << "Can not find root_val:" << root_val << std::endl;
        }
        // 切割中序,左闭右开
        int left_inorder_begin(inorder_begin), left_inorder_end(root_idx);
        int right_inorder_begin(root_idx + 1), right_inorder_end(inorder_end);
        // 切割后序,左闭右开
        int left_postorder_begin(postorder_begin), left_postorder_end(postorder_begin + (left_inorder_end - left_inorder_begin));
        int right_postorder_begin(left_postorder_end), right_postorder_end(postorder_end - 1);
        // 打印日志
        // std::cout << "left_inorder:" << std::endl;
        // for (int i = left_inorder_begin; i < left_inorder_end; ++i) {
        //     std::cout << inorder[i] << ' ';
        // }
        // std::cout << std::endl;
        // std::cout << "right_inorder:" << std::endl;
        // for (int i = right_inorder_begin; i < right_inorder_end; ++i) {
        //     std::cout << inorder[i] << ' ';
        // }
        // std::cout << std::endl;
        // std::cout << "left_postorder:" << std::endl;
        // for (int i = left_postorder_begin; i < left_postorder_end; ++i) {
        //     std::cout << postorder[i] << ' ';
        // }
        // std::cout << std::endl;
        // std::cout << "right_postorder:" << std::endl;
        // for (int i = right_postorder_begin; i < right_postorder_end; ++i) {
        //     std::cout << postorder[i] << ' ';
        // }
        // std::cout << std::endl;
        // 递归左右孩子
        root->left = BuildTreeFromInorderPostorder(inorder, left_inorder_begin, left_inorder_end, postorder, left_postorder_begin, left_postorder_end);
        root->right = BuildTreeFromInorderPostorder(inorder, right_inorder_begin, right_inorder_end, postorder, right_postorder_begin, right_postorder_end);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.empty() || postorder.empty() || inorder.size() != postorder.size()) {
            return nullptr;
        }
        return BuildTreeFromInorderPostorder(inorder, 0, inorder.size(), postorder, 0, postorder.size());
    }
};

【收获与时长】1h40m

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值