深搜(二叉树的所有路径)(6)

一.题目

257. 二叉树的所有路径 - 力扣(LeetCode)

二.思路讲解

2.1 选择遍历方式

本题要求返回从根节点到所有叶子节点 的路径,因此我们需要采用前序遍历 ,这样才能在访问节点时从根开始逐步向下构建路径。前序遍历的顺序是根-左-右,正好符合路径的生成顺序。

2.2 路径的构建与存储

在遍历过程中,我们需要一个数据结构来记录当前路径。通常使用一个字符串 path 来存储从根到当前节点的路径表示。对于普通节点,我们应将节点值转换为字符串,并加上 "->" 作为分隔;而对于叶子节点 ,则只需加上节点值即可,然后将完整路径存入结果数组 ret 中。这里,pathret 可以是全局变量或通过参数传递。

2.3 回溯与参数传递的权衡

如果采用全局变量 path,那么在递归返回时,需要手动恢复现场 (即删除刚刚添加的节点部分),否则路径会错误累积。例如,处理完一个叶子节点后,必须将最后添加的节点值及箭头弹出,以便后续路径使用。这种回溯操作容易遗漏,导致错误。

如果将 path 作为递归函数的参数 传递,则每次递归调用都会创建一个新的字符串副本,无需手动恢复现场,因为参数是值传递 ,不会影响上一层的 path。这种方式更安全。

2.4 递归终止条件与剪枝

为了简化问题,我们将叶子节点 作为递归的最简单情况 。当遇到叶子节点时,直接构建完整路径并存入结果,然后返回,不再继续递归。这样,我们就不需要递归到空节点,从而剪枝 掉空节点的判断,使代码更简洁高效。具体来说,在递归函数中,首先处理当前节点,如果当前节点是叶子节点,则记录路径并返回;否则,分别递归左子树和右子树(如果存在)。这样,我们通过提前终止避免了不必要的空节点递归。

三.代码演示

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    vector<string> ret;
    vector<string> binaryTreePaths(TreeNode* root) 
    {
        string path = "";
        dfs(root,path);
        return ret;
    }
    void dfs(TreeNode* root,string path)
    {
        //1.是叶子节点
        if(root->left == nullptr && root->right == nullptr)
        {
            path += to_string(root->val);
            ret.push_back(path);
            return;
        }
        //2.不是叶子节点
        path += to_string(root->val);
        path += "->";
        if(root -> left != nullptr)
            dfs(root->left,path);
        if(root -> right != nullptr)
            dfs(root->right,path);

    }
};

四.代码演示

一、递归函数设计

我们定义一个递归函数 dfs(TreeNode* root, string path) ,它的作用是:以当前节点 root 为起点,从根到该节点的路径已经记录在 path 中,继续向下遍历,并将所有从根到叶子节点的完整路径存入结果数组 ret。这里 path 采用值传递,这样每一层递归都有自己的副本,无需手动回溯。

二、递归终止条件(叶子节点处理)

本题将叶子节点 作为递归的最简单情况。当当前节点的左右子节点均为空时,说明到达叶子节点。此时,我们将当前节点的值追加到 path 中,形成完整路径,然后将其加入结果数组 ret,并返回。这样,我们无需递归到空节点 ,避免了不必要的递归调用,实现了剪枝

三、递归步骤分解(非叶子节点处理)

对于非叶子节点,我们需要继续向下遍历左右子树。步骤如下:

  1. 构建当前路径 :将当前节点的值转换为字符串,并加上 "->" 分隔符,追加到 path 末尾。

  2. 递归左子树 :如果左孩子存在,调用 dfs(root->left, path),传入更新后的路径。

  3. 递归右子树 :如果右孩子存在,调用 dfs(root->right, path)

注意,由于 path 是值传递,左右子树的递归调用使用的是不同的副本,因此不会相互干扰,也无需手动恢复现场。

四、参数传递与回溯

这里采用 path 作为函数参数(值传递),而不是全局变量。这样做的好处是:

  • 每次递归调用都会创建新的字符串副本,自动隔离不同分支的路径。

  • 避免了手动回溯(如 pop_back)的麻烦,降低了出错概率。

  • 虽然会产生一定的拷贝开销,但路径长度有限,完全可以接受。

如果使用全局变量,则需要在每次递归返回后手动删除刚刚添加的部分,这容易遗漏,而参数传递则天然解决了这个问题。

五、关键细节
  • 叶子节点的判断 :直接检查 root->left == nullptr && root->right == nullptr,因为题目保证每个节点要么有孩子要么没有。

  • 路径构建 :对于非叶子节点,需要添加 "->";对于叶子节点,只添加节点值,不加箭头。

  • 空节点剪枝:在递归前先判断孩子是否存在,避免了调用空节点,进一步优化。

  • 结果数组ret 作为成员变量,在递归过程中不断添加完整路径。

相关推荐
啦啦啦_99992 小时前
3. KNN算法之 常用的距离度量方式(欧式&曼哈顿&切比雪夫&闵式距离)
算法
GIS阵地2 小时前
QGIS的分类渲染核心类解析
c++·qgis·开源gis
朝风工作室2 小时前
实时全景拼接|支持任意路数输入,8*8K RTX3050 实测 10ms 内
图像处理·算法·计算机视觉
凯瑟琳.奥古斯特2 小时前
C++变量与基本类型精解
开发语言·c++
想唱rap2 小时前
UDP套接字编程
服务器·网络·c++·网络协议·ubuntu·udp
nianniannnn3 小时前
HNU计算机系统期中题库详解(五)位运算与逻辑运算
算法·位运算·计算机系统
来日可期13143 小时前
计算机存储视角下的有符号数:不止是“正负”那么简单
c++
愚者游世3 小时前
variadic templates(可变参数模板)各版本异同
开发语言·c++·程序人生·面试
徐新帅3 小时前
4181:【GESP2603七级】拆分
c++·学习·算法·信奥赛