Leetcode:257. 二叉树的所有路径(C++)

news2024/12/26 11:50:22

目录

问题描述:

实现代码与解析:

递归:

原理思路:

迭代(前序):

思路原理:


问题描述:

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

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

示例 1:

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

实现代码与解析:

递归:

class Solution {
public:
    void traversal(TreeNode* cur,vector<int> path,vector<string>& result)
    {
        //先将结点加入路径中
        path.push_back(cur->val);
        //到了叶子结点,记录整条路径,返回
        if(cur->left==NULL&&cur->right==NULL)
        {
            string paths;//记录整条路径
            //依据输出格式            
            for(int i=0;i<path.size()-1;i++)
            {
                paths+=to_string(path[i]);
                paths+="->";
            }
            paths+=to_string(path[path.size()-1]);//记录最后一个结点
            result.push_back(paths);
            return;//返回
        }
        if(cur->left) traversal(cur->left,path,result);
        if(cur->right) traversal(cur->right,path,result);
        return;
    }
    vector<string> binaryTreePaths(TreeNode* root) 
    {
        vector<int> path;//记录路径
        vector<string> result;//记录结果
        if(root==NULL) return result;
        traversal(root,path,result);
        return result;
    }
};

精简版:

class Solution {    
public:
    void traversal(TreeNode* cur, string path, vector<string>& result) {
            path += to_string(cur->val); 
            if (cur->left == NULL && cur->right == NULL) {
                result.push_back(path);
                return;
            }
            if (cur->left) traversal(cur->left, path + "->", result); 
            if (cur->right) traversal(cur->right, path + "->", result); 
        }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        string path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;

    }
};

原理思路:

        用一个数组path记录路径,一个数组result记录最终结果,每到一个结点就将结点值放入数组中,若碰到了叶子结点, 就将此时数组记录的路径传入结果result中,注意按照题目给的格式传入。精简版的代码就直接定义path为string类型的,每到一个结点就直接加一个"->"的符号,也是可以的,相对来说代码更简洁一些,还是很巧妙的。这里还用到了 to_string() 函数:

        to_string() 函数:将数字常量转换为字符串,返回值为转换完毕的字符串。

        我们判断完是否为叶子结点(也就是左右子树为空)后,需要再判断一下左右子树是否有一侧为空的,再决定向左子树还是右子树走,如下图:

        或者我们还按之前题目的遍历一样,走到结点后再判断是否为空,若为空,在记录结点前直接返回就可以,这样我们就不用在走之前判断一下了,就如下图的同样情况下:
 

这种处理方式的代码如下:

class Solution {    
public:
    void traversal(TreeNode* cur, string path, vector<string>& result) 
    {
        if(cur==NULL) return;//这里就是和上面代码的不同,也是可以通过的
        path += to_string(cur->val); 
        if (cur->left == NULL && cur->right == NULL) {
            result.push_back(path);
            return;
        }
        traversal(cur->left, path + "->", result); //不同处
        traversal(cur->right, path + "->", result); //不同处
    }
};

        显然第一种处理方法应该好一点。其实就是解决了一下一些人包括我自己的疑问,为什么我们之前有的题就是第二种返回方式,而这个题用的第一种返回方式,其实这题加上第二种方式也是可以的,只是具体实现代码有少些区别而已,只要我们想明白代码的执行过程,对于不同思路的具体写法就有了更深的认知,也就能在写不同的题的时候,明白我们用不同思路解题时那些可用,那些不可用,那些方法更好。

        这个递归方法也还有别的写法,就是将path以引用的方式传入,不过我们要在每次返回的时候把该结点再移出路径,因为我们要折回去向别的方向走了嘛。上面的代码直接用的是值传递,当返回时,path自动就变为上一层的值了,也就是这样写可以省略移出的这个步骤而已,思路是一样的,只是不同的写法而已,以值传递写虽然更简洁,但也需要你能够熟悉并理解它隐藏和省略的步骤。下面给出引用传递写此题的代码:

class Solution {    
public:
    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) 
    {
        path.push_back(cur->val); 
        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(); // 不同处
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        vector<int> path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
};

迭代(前序):

class Solution {    
public: 
    vector<string> binaryTreePaths(TreeNode* root) 
    {
        vector<string> result;//接收结果
        stack<TreeNode*> s1;//记录结点
        stack<string> s2;//记录遍历过的路径              
        if(root==NULL) return result;
        s1.push(root);
        s2.push(to_string(root->val));
        while(!s1.empty())
        {
            string path;//记录路径
            path+=s2.top();//取出该结点的路径
            s2.pop();
            TreeNode* temp=s1.top();
            s1.pop();//弹出结点
            if(temp->left==NULL&&temp->right==NULL) result.push_back(path);//到了叶子结点,path记入结果
            if(temp->right)
            {
                s1.push(temp->right);
                s2.push(path+"->"+to_string(temp->right->val));
            }
            if(temp->left)
            {
                s1.push(temp->left);
                s2.push(path+"->"+to_string(temp->left->val));
            }
        }
        return result;

    }
};

思路原理:

        其实就是用栈模拟了一下递归的步骤,这里看出还是递归好写,这里记录路径的步骤和遍历结点的步骤是一一对应的,因为记录路径的栈就是记录的对应结点的路径,当结点入栈,对应的路径就入栈,当结点出栈,对应的路径就出栈,记住这个规则,我们在写代码的时候就不会乱了,把上面代码的部分找出放在下面,你就知道我说的是什么意思了。

//一一对应之处,只截取了部分代码

//第一处
stack<TreeNode*> s1;//记录结点
stack<string> s2;//记录遍历过的路径

//第二处
s1.push(root);
s2.push(to_string(root->val));

//第三处
string path;//记录路径
path+=s2.top();//取出该结点的路径
s2.pop();
TreeNode* temp=s1.top();
s1.pop();//弹出结点

//第四处
s1.push(temp->right);
s2.push(path+"->"+to_string(temp->right->val));//与当前路径相加

//第五处
s1.push(temp->left);
s2.push(path+"->"+to_string(temp->left->val));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络第一章

目录 1.概念 2.标准化工作及其相关组织 3.速率相关的性能指标 4.分层的基本原则&#xff1a; 5.参考模型 1.OSI七层参考模型 2.TCP/IP参考模型 3.五层参考模型 1.概念 计算机网络是网络中的一个分支&#xff0c;组成包括了计算机系统&#xff0c;通信设备&#xff0c;线路…

app逆向 || xx合伙人登陆参数

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 本文适用于对安卓开发和Java有了解的同学 前言 本人最近一直在…

学习笔记5:关于操作符与表达式的求值

目录​​​​​​​ 一.移位操作符 1.左移操作符 2.右移操作符 二.位操作符 1.位运算基本知识 2.位运算的巧妙运用 三.其他操作符 1.算术操作符 2.单目操作符 3.关于逻辑操作符 四.表达式求值 隐式类型转换 (1)整形提升(短整型家族数据的二进制序列补位转换) (2).算…

【最新】SpringBoot集成Dubbo3

最近在学习dubbo&#xff0c;构建一个简单的springboot集成dubbo&#xff0c;中间也是出了好多问题&#xff0c;在这记录下整体的过程。 1. 构建SpringBoot环境 一个简单的聚合工程 dubbo-consumer&#xff1a;是服务消费方dubbo-provider&#xff1a;是服务提供方dubbo-inte…

机器学习笔记之前馈神经网络(二)非线性问题

机器学习笔记之前馈神经网络——非线性问题引言回顾&#xff1a;关于非线性问题解决非线性问题的三种方式引言 上一节介绍了从机器学习到深度学习的过渡&#xff0c;并介绍了深度学习的发展过程。本节将主要介绍如何使用神经网络处理非线性问题 回顾&#xff1a;关于非线性问…

决策树生成、决策树可视化、决策树算法api、泰坦尼克号乘客生存预测案例代码

一、决策树算法api class sklearn.tree.DecisionTreeClassifier(criterion’gini’,max_depthNone,random_stateNone) criterion&#xff1a;特征选择标准&#xff0c;"gini"或者"entropy"&#xff0c;前者代表基尼系数&#xff0c;后者代表信息增益&…

来自 GitHub 2022 的趋势和见解

《Github 2022 发展趋势和见解》发布了这件事小伙伴们知道了吧&#xff1f;这是每个程序员不能错过的年度报告&#xff0c;因为里面详细介绍了语言的发展趋势和热门领域的介绍。那就让我们来看看吧 目录 编程语言 地理分布 贡献时间分配 技术发展趋势 最受欢迎的存储库 …

GoogLeNet详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、GoogLeNet…

C++入门——auto、范围for、nullptr

下一篇就要类和对象了&#xff0c;剩了点零碎的知识点就浅浅水一篇把 一. auto关键字 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;但遗憾的 是一直没有人去使用它&#xff0c;这是由于变量本身就具备生命周期…

算法及时间、空间复杂度

算法 算法是对问题求解过程的一种描述&#xff0c;是为解决一个或一类问题给出的一个确定的、有限长的操作序列。严格说来&#xff0c;一个算法必须满足以下5个重要特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;对于任意一组合法的输入值&#xff0c;在执行有…

【数据结构与算法——C语言版】5. 排序算法(2)——冒泡排序

前言 上篇文章【数据结构与算法——C语言版】4. 排序算法&#xff08;1&#xff09;——选择排序我们介绍了排序算法中的选择排序&#xff0c;其时间复杂度是O(n2)&#xff0c;本篇文章我们将介绍另一种同样时间复杂度是O(n2)的排序算法——冒牌排序&#xff0c;这两种算法思路…

ChatGPT背后的开源AI框架Ray,现在值10亿美元

Ray 被 OpenAI、亚马逊等科技公司用来开发大模型&#xff0c;是最近异军突起的框架。 最近一段时间&#xff0c;文本生成的人工智能在互联网上掀起了一阵风暴&#xff1a;ChatGPT 因为可以对人们能想到的几乎任何问题提供非常详细、近乎逼真的回答而受到追捧。大模型应用的出现…

Mapper代理开发案例及MyBatis核心

本片文章需要参考我的前一篇文章&#xff1a;MyBatis入门案例引入总结&#xff0c;使用mapper代理开发的好处就是可以解决开发中硬编码的问题和简化后期的SQL执行。使用这种方式可以不用写接口的实现类&#xff0c;免除了复杂的方法&#xff0c;使得代码更加清晰易懂按照以前的…

vue的过渡动画(有vue的动画库和ui库的介绍)

一、概念 Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。 二、默认过渡 <template><div><button click"isShow!isShow">显示/隐藏</button><transition appear><h1 v-show"isShow" cl…

过滤器和拦截器的使用及管理

参考&#xff1a;(70条消息) Spring过滤器和拦截器的区别_yjc0403的博客-CSDN博客https://www.cnblogs.com/colin220/p/9606412.htm概述过滤器&#xff1a;是在javaweb中&#xff0c;你传入的request、response提前过滤掉一些信息&#xff0c;或者提前设置一些参数&#xff0c;…

Anaconda安装之后Spyder打不开解决办法--目前有用 jupyter notebook 无法正常运行2023.1.7

纯纯小白&#xff0c;探索一天&#xff0c;终于成功&#xff0c;需要我的经历没有白费&#xff0c;让大家少走弯路。 问题描述 从官网下载Anaconda之后&#xff0c;安装&#xff0c;一切正常。打开Anaconda navigator在弹出窗口选择了更新&#xff08;我怀疑这就根源&#xf…

Js逆向教程24-作用域和自执行函数

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; Js逆向教程24-作用域和自执行函数 一、变量作用域 1.1局部变量 function jb() {var a"我是局部变量"return a; }1.2全局变…

【Java寒假打卡】Java基础-异常

【Java寒假打卡】Java基础-异常异常概述throws声明异常throw抛出异常try-catch 抛出异常throwable的成员方法异常概述 Exception:称之为异常类&#xff0c;他表示程序本身可以处理的问题 RuntimeException及其子类&#xff1a;运行时异常。&#xff08;空指针异常&#xff0c;…

JUC总结系列篇 (二) : 对线程的理解和使用总结

文章内容&#xff1a; 一.为什么需要多线程 二.线程的创建 三.线程的方法sleep(),run(),wait(),yeid(),join(),interrupt()等方法归纳总结 四.线程的状态及其转换 五.线程的交替执行案例 六.多个线程依次执行案例 七.多线程并发带来的线程安全问题 一.为什么需要多线程&#x…

Linux项目自动化构建工具-make/Makefile

一、前言 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力。一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件…