二叉树所有路径

力扣题目链接(opens new window)

给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

示例:

思路

根据题意,每次遍历至子节点,我们都需要返回根节点然后从另外一条路径继续遍历

关键点是:返回,实现这个机制需要使用递归与回溯

且最后输出结果的顺序是根节点->子节点->叶子节点,也就是从上到下的顺序,因此可以使用前序遍历

代码

先写递归逻辑,还是递归的三部曲

1、确定递归函数的参数、返回值

需要传入的是根节点,然后记录每一条路径path,并保存至结果数组res;

不涉及返回值

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){

}

在记录路径时,一开始我们是通过递归不断保存路径上每个节点的值,因此需要使用数组来保存

之后按照题目要求,输出时需要将一段路径用"->"连接,因此结果数组中保存的应该是字符串类型的路径

2、确定终止条件

由题意可知,从根节点到叶子节点才算构成一条完整路径

那么我们的终止条件可以设置为:当前节点是否为叶子节点

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){
if(node->left == NULL && node->right == NULL){//左右子节点均为空时
//终止时的处理代码
}
}

因为使用vector来存放路径,所以在结束一条路径的遍历时,我们需要将path数组的值遍历出来,转换为string并拼接箭头

最后存放至res结果数组中。

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){
if(node->left == NULL && node->right == NULL){//左右子节点均为空时
//终止时的处理代码
//定义一个string变量存放路径字符串
string spath;
//遍历path数组
for(int i = 0; i < path.size() - 1; ++i){
spath += to_string(path[i]);//将元素转换为字符串类型
spath += "->";//拼接箭头
}
//单独把最后一个节点(叶子节点)接上,因为其后不需要接箭头,所以不放在循环里
spath += to_string(path[path.size() - 1]);
//保存一条路径至结果数组
res.push_back(spath);
return;//结束
}
}

3、确定单层处理逻辑

这里的遍历逻辑是前序遍历,中左右

按理来说,“中”(处理中间节点)应该和“左右”一块放在终止条件后面

但是,这里情况比较特殊,中间节点就是我们需要记录的路径节点,因此需要先将其放入path中,即

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){
path.push_back(cur->val);//将当前节点值放入path
if(node->left == NULL && node->right == NULL){//左右子节点均为空时
...
}
}

然后,左右的遍历处理与往常一样,不过得先判断左右子节点是否存在

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){
path.push_back(cur->val);//将当前节点值放入path
if(node->left == NULL && node->right == NULL){//左右子节点均为空时
//终止时的处理代码
//定义一个string变量存放路径字符串
string spath;
//遍历path数组
for(int i = 0; i < path.size() - 1; ++i){
spath += to_string(path[i]);//将元素转换为字符串类型
spath += "->";//拼接箭头
}
//单独把最后一个节点(叶子节点)接上,因为其后不需要接箭头,所以不放在循环里
spath += to_string(path[path.size() - 1]);
//保存一条路径至结果数组
res.push_back(spath);
return;//结束
} if(node->left != NULL){
traversal(node->left, path, res);
} if(node->right != NULL){
traversal(node->right, path, res);
}
}

然后,重要的点来了

前面我们在梳理思路的时候分析了,当找到叶子节点后,我们就在path数组中记录了一条完整路径

那么此时我们需要将记录的节点“弹出”,直到只剩下根节点,然后再去寻找系下一条路径

上述过程即为 回溯

如何实现回溯呢?我们在前序遍历中遍历左右子节点时使用了 递归 ,那么当递归一层一层的执行,最后我们会找到某个叶子节点

此时按照递归的逻辑,最内层的递归会将获取到的结果层层返回

因为我们在最里层递归中已经将完整路径字符串保存到结果数组,所以我们可以利用递归返回值的过程,将每层递归记录的路径节点逐个pop掉

最后只剩下根节点,然后开始下一条路径的遍历

void  traversal(TreeNode* node, vector<int>& path, vector<string>& res){
path.push_back(node->val);//将当前节点值放入path
//左右子节点均为空时找到叶子节点
if(node->left == NULL && node->right == NULL){//已终止递归,开始保存记录的路径节点
//终止时的处理代码
//定义一个string变量存放路径字符串
string spath;
//遍历path数组
for(int i = 0; i < path.size() - 1; ++i){//减一是因为最后一个节点需要在循环外处理
spath += to_string(path[i]);//将元素转换为字符串类型
spath += "->";//拼接箭头
}
//单独把最后一个节点(叶子节点)接上,因为其后不需要接箭头,所以不放在循环里
spath += to_string(path[path.size() - 1]);
//保存一条路径至结果数组
res.push_back(spath);
return;//结束
} if(node->left != NULL){
traversal(node->left, path, res);
res.pop_back();//在递归返回过程中(回溯)不断删除之前记录的路径节点
} if(node->right != NULL){
traversal(node->right, path, res);
res.pop_back();
}
}

完整代码

class Solution {
public:
//创建递归函数
//确定递归函数的参数和返回值
void traversal(TreeNode* node, vector<int>& path, vector<string>& res){
path.push_back(node->val);//将当前节点的值放入path //中
//确定终止条件
//左右子节点均为空时找到叶子节点
if(node->left == NULL && node->right == NULL){//已终止递归,开始保存记录的路径节点
//定义变量保存路径字符串
string spath;
//遍历path取出记录的路径节点
for(int i = 0; i < path.size() - 1; ++i){//减一是因为最后一个节点需要在循环外处理
spath += to_string(path[i]);//将元素转换为字符串类型
spath += "->";//使用箭头拼接
}
//单独把最后一个节点(叶子节点)接上,因为其后不需要接箭头,所以不放在循环里
spath += to_string(path[path.size() - 1]);
//保存一条完整路径
res.push_back(spath);
return;
}
//确定单层处理逻辑
if(node->left){//左
traversal(node->left, path, res);
path.pop_back();//在递归返回过程中(回溯)不断删除之前记录的路径节点
}
if(node->right){//右
traversal(node->right, path, res);
path.pop_back();
}
} vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> res;
traversal(root, path, res);
return res;
}
};

注意点:

1、真的,别忘了输入参数时以引用方式输入

2、一般在递归三部曲的第三步:确认当前递归层的处理逻辑中,都会再次调用递归函数

【LeetCode二叉树#06】获取二叉树的所有路径(分析递归中的回溯机制)的更多相关文章

  1. php 遍历文件夹及文件,获取文件名和文件路径存入数据库中

    <?php header("Content-Type:text/html; charset=gbk"); require('../../include/connect.php ...

  2. 代码随想录算法训练营day17 | leetcode ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

    LeetCode 110.平衡二叉树 分析1.0 求左子树高度和右子树高度,若高度差>1,则返回false,所以我递归了两遍 class Solution { public boolean is ...

  3. 二叉树 Java 实现 前序遍历 中序遍历 后序遍历 层级遍历 获取叶节点 宽度 ,高度,队列实现二叉树遍历 求二叉树的最大距离

    数据结构中一直对二叉树不是很了解,今天趁着这个时间整理一下 许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显 ...

  4. leetcode算法笔记:二叉树,动态规划和回溯法

    在二叉树中增加一行 题目描述 给定一个二叉树,根节点为第1层,深度为 1.在其第 d 层追加一行值为 v 的节点. 添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N, ...

  5. LeetCode入门指南 之 二叉树

    二叉树的遍历 递归: void traverse (TreeNode root) { if (root == null) { return null; } //前序遍历位置 traverse(root ...

  6. Leetcode 101 Symmetric Tree 二叉树

    判断一棵树是否自对称 可以回忆我们做过的Leetcode 100 Same Tree 二叉树和Leetcode 226 Invert Binary Tree 二叉树 先可以将左子树进行Invert B ...

  7. [LeetCode] Binary Tree Pruning 二叉树修剪

    We are given the head node root of a binary tree, where additionally every node's value is either a ...

  8. 【LeetCode题解】94_二叉树的中序遍历

    目录 [LeetCode题解]94_二叉树的中序遍历 描述 方法一:递归 Java 代码 Python代码 方法二:非递归 Java 代码 Python 代码 [LeetCode题解]94_二叉树的中 ...

  9. 【LeetCode题解】144_二叉树的前序遍历

    目录 [LeetCode题解]144_二叉树的前序遍历 描述 方法一:递归 Java 代码 Python 代码 方法二:非递归(使用栈) Java 代码 Python 代码 [LeetCode题解]1 ...

  10. 【js】Leetcode每日一题-二叉树的堂兄弟节点

    [js]Leetcode每日一题-二叉树的堂兄弟节点 [题目描述] 在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处. 如果二叉树的两个节点深度相同,但 父节点不 ...

随机推荐

  1. [转帖]网卡多队列:RPS、RFS、RSS、Flow Director(DPDK支持)

    Table of Contents 多队列简介 RPS介绍(Receive Packet Steering) RFS介绍(Receive flow steering) RSS介绍(receive si ...

  2. 一次JSF上线问题引发的MsgPack深入理解,保证对你有收获

    作者: 京东零售 肖梦圆 前序 某一日晚上上线,测试同学在回归项目黄金流程时,有一个工单项目接口报JSF序列化错误,马上升级对应的client包版本,编译部署后错误消失. 线上问题是解决了,但是作为程 ...

  3. React Hooks源码深度解析

    作者:京东零售 郑炳懿 前言 React Hooks是React16.8 引入的一个新特性,它允许函数组件中使用state和其他 React 特性,而不必使用类组件.Hooks是一个非常重要的概念,因 ...

  4. JAVA多线程并发编程-避坑指南

    作者:京东零售 肖朋伟 一.前言 开发过程中,多线程的应用场景可谓十分广泛,可以充分利用服务器资源,提高程序处理速度.我们通常也会使用池化技术,去避免频繁创建和销毁线程. 本篇旨在基于编码规范.工作中 ...

  5. P5963 [BalticOI ?] Card 卡牌游戏【来源请求】

    [rt](https://www.luogu.com.cn/problem/P5963)------------## part1### 题意简述给你 $n$ 张纸牌,每张纸牌有两个面.将 $n$ 张纸 ...

  6. IM开源项目OpenIM部署文档-从准备工作到nginx配置

    IM开源项目OpenIM部署文档-从准备工作到nginx配置 2022-11-14 22:27·OpenIM 一.准备工作 运行环境 linux系统即可, Ubuntu 7.5.0-3ubuntu1~ ...

  7. LyScript 实现绕过反调试保护

    LyScript插件中内置的方法可实现各类反调试以及屏蔽特定API函数的功能,这类功能在应对病毒等恶意程序时非常有效,例如当程序调用特定API函数时我们可以将其拦截,从而实现保护系统在调试时不被破坏的 ...

  8. STM32CubeMX教程29 USB_HOST - 使用FatFs文件系统读写U盘

    1.准备材料 正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 ...

  9. 【Trento】遥感图像数据集提供下载

    遥感图像处理学习(11)之Trento数据集 前言 遥感系列第11篇.遥感图像处理方向的学习者可以参考或者复刻 本文初编辑于2024年1月18日 2024年1月25日搬运至本人博客园平台 最近在复现论 ...

  10. Apache和Nginx是什么?|Nginx和Reactor是什么?|网路IO的本质|阻塞队列|异步非阻塞IO

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量干货博客汇总https://blog. ...