首页 > 其他分享 >【257. 二叉树的所有路径 简单】

【257. 二叉树的所有路径 简单】

时间:2024-12-28 12:54:54浏览次数:8  
标签:cur 递归 路径 节点 result 回溯 path 257 二叉树

题目:

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:
在这里插入图片描述
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]

示例 2:
输入:root = [1]
输出:[“1”]

提示:

  • 树中节点的数目在范围 [1, 100] 内
  • -100 <= Node.val <= 100

思路:

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。

在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。

前序遍历以及回溯的过程如图:
257.二叉树的所有路径

我们先使用递归的方式,来做前序遍历。要知道递归和回溯就是一家的,本题也需要回溯。

  1. 递归法
  • 递归函数参数以及返回值

    要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值,代码如下:

    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result)
    
  • 确定递归终止条件

    在写递归的时候都习惯了这么写:

    if (cur == NULL) {
    	终止处理逻辑
    }
    

    但是本题的终止条件这样写会很麻烦,因为本题找到叶子节点时就开始结束的处理逻辑了(把路径放进result里)。

    那么什么时候算是找到了叶子节点? 是当 cur不为空,其左右孩子都为空的时候,就找到叶子节点。

    所以本题的终止条件是:

    if (cur->left == NULL && cur->right == NULL) {
    	终止处理逻辑
    }
    

    为什么没有判断cur是否为空呢,因为下面的逻辑可以控制空节点不入循环。

    再来看一下终止处理的逻辑。

    这里使用vector 结构path来记录路径,所以要把vector 结构的path转为string格式,再把这个string 放进 result里。

    那么为什么使用了vector 结构来记录路径呢? 因为在下面处理单层递归逻辑的时候,要做回溯,使用vector方便来做回溯。

    可能有的同学问了,有些人的代码也没有回溯啊。

    其实是有回溯的,只不过隐藏在函数调用时的参数赋值里,下文还会提到。

    这里我们先使用vector结构的path容器来记录路径,那么终止处理逻辑如下:

    if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点
    string sPath;
    for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
        sPath += to_string(path[i]);
        sPath += "->";
    }
    sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)
    result.push_back(sPath); // 收集一个路径
    return;
    }
    
  • 确定单层递归逻辑
    因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。

    path.push_back(cur->val);
    

    然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。

    所以递归前要加上判断语句,下面要递归的节点是否为空,如下

    if (cur->left) {
    	traversal(cur->left, path, result);
    }
    if (cur->right) {
    	traversal(cur->right, path, result);
    }
    

    此时还没完,递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。

    那么回溯要怎么回溯呢,一些同学会这么写,如下:

    if (cur->left) {
    	traversal(cur->left, path, result);
    }
    if (cur->right) {
    	traversal(cur->right, path, result);
    }
    path.pop_back();
    

    这个回溯就有很大的问题,我们知道,回溯和递归是一一对应的,有一个递归,就要有一个回溯,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。

    所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!

    那么代码应该这么写:

    if (cur->left) {
    	traversal(cur->left, path, result);
    	path.pop_back(); // 回溯
    }
    if (cur->right) {
    	traversal(cur->right, path, result);
    	path.pop_back(); // 回溯
    }
    
  1. 迭代法

    至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程。

    这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。


代码:

  1. 递归法
class Solution {
private:

    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
        path.push_back(cur->val); // 中,中为什么写在这里,因为最后一个节点也要加入到path中 
        // 这才到了叶子节点
        if (cur->left == NULL && cur->right == NULL) {
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) {
                sPath += to_string(path[i]);
                sPath += "->";
            }
            sPath += to_string(path[path.size() - 1]);
            result.push_back(sPath);
            return;
        }
        if (cur->left) { // 左 
            traversal(cur->left, path, result);
            path.pop_back(); // 回溯
        }
        if (cur->right) { // 右
            traversal(cur->right, path, result);
            path.pop_back(); // 回溯
        }
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        vector<int> path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
};
  1. 迭代法
class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        stack<TreeNode*> treeSt;   //  保存树的遍历节点
        stack<string> pathSt;   //  保存遍历路径的节点
        vector<string> result;  //  存储所有路径

        if(root == NULL) return result;
        treeSt.push(root);
        pathSt.push(to_string(root->val));

        while(!treeSt.empty()){
            TreeNode* node = treeSt.top();  treeSt.pop();   //  中 取出节点
            string path = pathSt.top();    pathSt.pop();   //  取出该节点对应的路径

            if(node->left == NULL && node->right == NULL){
                result.push_back(path);                
            }
            
            if(node->right){
                treeSt.push(node->right);
                pathSt.push(path + "->" + to_string(node->right->val));  //  右
            }
            if(node->left){
                treeSt.push(node->left);
                pathSt.push(path + "->" + to_string(node->left->val));   //  左
            }
        }
        return result;
    }
};

总结:

本文我们开始初步涉及到了回溯,很多同学过了这道题目,可能都不知道自己其实使用了回溯,回溯和递归都是相伴相生的。


参考:

代码随想录

标签:cur,递归,路径,节点,result,回溯,path,257,二叉树
From: https://blog.csdn.net/yuan_2001_/article/details/144787394

相关文章

  • 磁盘剩余空间大于80%时,删除某个路径下的文件
    #使用Shell脚本定期清理包含“202”的目录##简介使用Shell脚本检查磁盘使用情况,并自动删除路径下包含“202”的目录及其内容,。##实现创建一个简单的Shell脚本:1.**检查磁盘使用情况**:使用`df`命令获取当前磁盘使用率。2.**条件判断**:如果磁盘使用率超过设定阈值(例如......
  • 通过在 组策略管理控制台 中配置 AppLocker,可以非常有效地限制 PowerShell 脚本的执行
    在组策略管理控制台(GroupPolicyManagementConsole,GPMC)中配置AppLocker,可以有效地限制和控制哪些应用程序(包括PowerShell脚本)可以在计算机上执行。这是一种通过白名单策略确保只有已批准的应用程序能够运行的强大安全措施。配置AppLocker的步骤:1. 打开组策略管理控制......
  • Springboot医院信管系统y2577
    Springboot医院信管系统y2577本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能:用户,医生,科室,医生信息,挂号信息,取消挂号,问诊记录开题报告内容基于Springboot的医院信息管理系统开题报告一、......
  • 以旧换新政策中的数字化协作路径探索
    政府发布的《汽车以旧换新补贴政策》为促进汽车消费、优化车辆结构提供了强有力的支持。然而,这一政策的落地实施,涉及多方协同,包括政策文件解读、销售渠道配合、消费者补贴申请等复杂流程。为了应对这些流程的高效管理,企业需要借助数字化协同工具,提升内部及外部的工作效率。在线协......
  • PyCharm专项训练5 最短路径算法
    一、实验目的    本文的实验目的是通过编程实践,掌握并应用Dijkstra(迪杰斯特拉)算法和Floyd(弗洛伊德)算法来解决图论中的最短路径问题。二、实验内容数据准备:使用邻接表的形式定义两个图graph_dijkstra和graph_floyd,图中包含节点以及节点之间的边和对应的权重。算......
  • 【无人机】无人机测绘路径优化策略与实践:探索高效、精准的测绘技术路径
    ......
  • 代码随想录——贪心23监控二叉树
    思路这道题目首先要想,如何放置,才能让摄像头最小的呢?从题目中示例,其实可以得到启发,我们发现题目示例中的摄像头都没有放在叶子节点上!这是很重要的一个线索,摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。所以把摄像头放在叶子节点的父节点位置,才......
  • 欧拉路径
    1736年29岁的欧拉向圣彼得堡科学院递交了《哥尼斯堡的七座桥》的论文,在解答问题的同时,开创了数学的一个新的分支——图论与拓扑学,也由此展开了数学史上的新历程。七桥问题提出后,很多人对此很感兴趣,纷纷进行试验,但在相当长的时间里,始终未能解决。欧拉通过对七桥问题的研究,不仅圆满......
  • 在写页面布局中,遇到链接时使用相对路径还是绝对路径好?为什么?
    在前端开发中,选择相对路径还是绝对路径主要取决于项目的具体需求和上下文。以下是关于相对路径和绝对路径的一些考虑因素:相对路径优点:灵活性:相对路径允许项目更灵活地被部署在不同的域或子目录下,而无需修改链接。可移植性:如果你的项目需要从一个服务器迁移到另一个服务器,或者......
  • 二叉树和哈希表
    二叉树二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。二叉树的递归定义为:二叉树或者是一棵空树,或者是一棵由一个根节点和两棵互不相交的分别称作根的左子树和右子树所组成的非空树,左子树和右子树又同样都是二叉树。下面相关代码实现都利用了......