代码随想录算法训练营day14 | 144.二叉树的前序遍历、145.二叉树的后序遍历、94.二叉树的中序遍历

理论基础

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

二叉树的遍历方式

二叉树主要有两种遍历方式:

  • 深度优先遍历:先往深走,遇到叶子节点再往回走。
  • 广度优先遍历:一层一层的去遍历。

深度优先遍历

  • 前序遍历(递归法,迭代法)
  • 中序遍历(递归法,迭代法)
  • 后序遍历(递归法,迭代法)

广度优先遍历

  • 层次遍历(迭代法)

栈其实是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的。

而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

二叉树的定义

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

递归算法的三个要素

确定递归函数的参数和返回值

 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

确定终止条件

 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

确定单层递归的逻辑

 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

二叉树的递归遍历

递归遍历代码比较统一,只有几行调换了顺序

前序遍历

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, res):
        if not cur:
            return
        res.append(cur.val)
        self.traversal(cur.left, res)
        self.traversal(cur.right, res)

中序遍历

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, res):
        if not cur:
            return
        self.traversal(cur.left, res)
        res.append(cur.val)
        self.traversal(cur.right, res)

后序遍历

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        self.traversal(root, res)
        return res

    def traversal(self, cur, res):
        if not cur:
            return
        self.traversal(cur.left, res)
        self.traversal(cur.right, res)
        res.append(cur.val)

二叉树的迭代遍历

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。 因此我们用栈也可以是实现二叉树的前后中序遍历了。

前序遍历

前序遍历(迭代法)
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 前序遍历,根左右。先遍历根,可以把根写入结果,然后把右节点加入栈,先去遍历左节点
        res = []
        stack = []
        cur = root
        while cur:
            # 先遍历根节点
            res.append(cur.val)
            if cur.right:
                stack.append(cur.right)
            if cur.left:
                stack.append(cur.left)
            if stack:
                cur = stack.pop()
            else:
                break
        return res

看题解之后优化的程序
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 前序遍历,根左右。先遍历根,可以把根写入结果,然后把右节点加入栈,先去遍历左节点
        res = []
        if not root:
            return res
        stack = [root]
        while stack:
            node = stack.pop()
            res.append(node.val)
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)

        return res

中序遍历

前序遍历迭代法,要访问的元素和要处理的元素顺序是一致的,都是中间节点。而中序遍历迭代法,处理顺序和访问顺序是不一致的。

那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。 指针顺着左节点遍历下去,同时使用栈存储路径中的节点,路径结束后将栈中元素拿出来处理,然后处理该元素的右节点

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 中序遍历为左根右
        res = []
        stack = []
        cur = root
        while stack or cur:
            if cur:
                stack.append(cur)
                cur = cur.left  # 左
            else:
                cur = stack.pop()
                res.append(cur.val)  # 根
                cur = cur.right  # 右

        return res

后序遍历

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 前序遍历为根左右,后序遍历为左右根。
        # 将前序遍历的顺序改为根右左,翻转之后为左右根,即为后序遍历
        res = []
        if not root:
            return res
        stack = [root]
        while stack:
            node = stack.pop()
            res.append(node.val)
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
        res.reverse()
        return res

二叉树的统一迭代遍历

二叉树的统一迭代法 将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。这种方法也可以叫做标记法

前序遍历

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 前序遍历,根左右。入栈顺序为右左根
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node:
                if node.right:  # 右
                    stack.append(node.right)
                if node.left:  # 左
                    stack.append(node.left)
                stack.append(node)  # 根
                stack.append(None)
            else:
                node = stack.pop()
                res.append(node.val)

        return res

中序遍历

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 中序遍历为左根右,那么放入栈的顺序为右根左
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node:
                if node.right:  # 右
                    stack.append(node.right)
                stack.append(node)  # 根
                stack.append(None)
                if node.left:  # 左
                    stack.append(node.left)
            else:
                node = stack.pop()
                res.append(node.val)

        return res

后序遍历

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        # 后序遍历为左右根,入栈顺序为根右左
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node:
                stack.append(node)  # 根
                stack.append(None)
                if node.right:  # 右
                    stack.append(node.right)
                if node.left:  # 左
                    stack.append(node.left)
            else:
                node = stack.pop()
                res.append(node.val)
        return res

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Git】回滚旧提交版本且不影响最新提交版本

【Git】回滚旧提交版本且不影响最新提交版本 一、场景假设 远程仓库origin中有一个分支main,有4次提交记录:v1、v2、v3、v4。 二、需求 需要回滚旧提交版本,但不影响已有的所有提交版本(即不影响最新提交版本)&…

树和二叉树:二叉树的基本运算算法的实现

一.前言 当前版本仅供笔者复盘 二.二叉树 2.1题目 编写一个程序,实现二叉树的基本运算,具体要求如下:(指定示范实例1:图1。指定示范实例2:图2 ) 1,先序遍历输出该树&#xff08…

PWM 开发舵机SG90-硬件舵机实战

1.PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节…

hadoop学习---基于Hive的数仓搭建增量信息拉链表的实现

拉链表就是SCD2,它的优点是即满足了反应数据的历史状态,又能在最大程度上节省存储。 拉链表的实现需要在原始字段基础上增加两个新字段: start_time(表示该条记录的生命周期开始时间——周期快照时的状态)end_time(该条记录的生命周期结束时…

JSP合同信息管理系统参考论文(论文 + 源码)

【免费】JSP合同信息管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89273651JSP合同信息管理系统 摘要 随着信息科学技术的飞速发展,人们逐渐意识到对信息管理软件的运用可以使日常工作更加方便、快捷和高效。论文详细论述了公司合同管理系…

Mysql 8.0 -- 最新版本安装(保姆级教程)

Mysql 8.0 -- 最新版本安装(保姆级教程) ​​ 一,下载Mysql数据库: 官网链接:https://www.mysql.com/downloads/ 二,安装Mysql: 三,找到Mysql安装目录: 找到mysql安装目录&#xf…

在k8s中安装Grafana并对接Prometheus,实现k8s集群监控数据的展示

🐇明明跟你说过:个人主页 🏅个人专栏:《Grafana:让数据说话的魔术师》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Grafana简介 2、Grafana的重要性与影响力 …

C进阶--自定义类型

自定义类型 1. 结构体1.1. 结构的基本知识1.2 结构的声明1.3 特殊的声明1.4 结构的自引用1.5 结构体变量的定义和初始化1.6 结构体内存对齐结构体的大小练习结构体对齐规则为什么存在内存对齐? 1.7 修改默认对齐数1.8 结构体传参 2. 位段2.1 什么是位段2.2 位段的内存分配2.3 …

MWeb Pro for Mac:功能强大的Markdown博客编辑器

MWeb Pro for Mac是一款功能强大的Markdown博客编辑器,专为Mac用户设计,提供了一站式的博客写作和发布体验。这款软件不仅支持Markdown语法,还提供了丰富的编辑和排版功能,让用户能够轻松创建出精美的博客内容。 MWeb Pro的即时预…

笔试强训Day16 字符串 基础算法 双指针

QR6 字符串替换 题目链接&#xff1a;字符串替换_牛客题霸_牛客网 (nowcoder.com) 思路&#xff1a;简单的字符串操作。 AC code&#xff1a; class StringFormat { public:string formatString(string A, int n, vector<char> arg, int m) {string ans;int pos 0;f…

【Qt】按钮类控件

文章目录 1 :peach:Push Button:peach:2 :peach:Radio Buttion:peach:3 :peach:Check Box:peach:4 :peach:Tool Button:peach: 1 &#x1f351;Push Button&#x1f351; 使⽤ QPushButton 表⽰⼀个按钮&#xff0c;这也是当前我们最熟悉的⼀个控件了&#xff0c;QPushButton …

论文阅读:《Sequence can Secretly Tell You What to Discard》,减少推理阶段的 kv cache

目前各类大模型都支持长文本&#xff0c;例如 kimi chat 以及 gemini pro&#xff0c;都支持 100K 以及更高的上下文长度。但越长的上下文&#xff0c;在推理过程中需要存储的 kv cache 也越多。假设&#xff0c;数据的批次用 b 表示&#xff0c;输入序列的长度仍然用 s 表示&a…

3.栈和队列(汇总版)

目录 1.栈&#xff08;一端插和删&#xff09; 2.队列&#xff08;一端插另一段删&#xff09; 2.1队列的概念及结构 2.2 队列的实现 队列的接口 1.初始化队列 2.销毁队列 3.插入元素 4.出队列&#xff08;头删&#xff09; 5.访问对头 6.访问队尾 7.判断队列是否为…

基于springboot实现的疫情网课管理系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

证明力引导算法forceatlas2为什么不是启发式算法

一、基本概念 吸引力 F a ( n i ) ∑ n j ∈ N c t d ( n i ) ω i , j d E ( n i , n j ) V i , j \displaystyle \bm{F}_a(n_i) \sum_{n_j \in \mathcal{N}_{ctd}(n_i)} \omega_{i,j} \; d_E(n_i,n_j) \bm{V}_{i,j} Fa​(ni​)nj​∈Nctd​(ni​)∑​ωi,j​dE​(ni​,nj​…

【StarRocks系列】 Trino 方言支持

我们在之前的文章中&#xff0c;介绍了 Doris 官方提供的两种方言转换工具&#xff0c;分别是 sql convertor 和方言 plugin。StarRocks 目前同样也提供了类似的方言转换功能。本文我们就一起来看一下这个功能的实现与 Doris 相比有何不同。 一、Trino 方言验证 我们可以通过…

C语言 | Leetcode C语言题解之第73题矩阵置零

题目&#xff1a; 题解&#xff1a; void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {int m matrixSize;int n matrixColSize[0];int flag_col0 false;for (int i 0; i < m; i) {if (!matrix[i][0]) {flag_col0 true;}for (int j 1; j < n; j…

C++:多继承虚继承

在C中&#xff0c;虚继承&#xff08;Virtual Inheritance&#xff09;是一种特殊的继承方式&#xff0c;用于解决菱形继承&#xff08;Diamond Inheritance&#xff09;问题。菱形继承指的是一个类同时继承自两个或更多个具有共同基类的类&#xff0c;从而导致了多个实例同一个…

20240507最新 ubuntu20.04安装ros noetic

ubuntu20.04安装ros 主要参考博客 【ROS】在 Ubuntu 20.04 安装 ROS 的详细教程_ubuntu20.04安装ros-CSDN博客 出现问题 1.ubuntu20.04 更换清华源报错 ubuntu20.04 更换清华源报错_gvfs metadata is not supported. fallback to teplme-CSDN博客 &#xff1f;&#xff1f…

汽车 - 什么是车轮抱死

车轮抱死分为两种情况&#xff0c;一种是车辆故障层面&#xff0c;另一种是驾驶过程中的物理现象。我们先来说最通俗的刹车车轮抱死吧。 刹车制动车轮抱死 车轮停止轴向转动就是抱死&#xff0c;有速度的情况下抱死车轮&#xff0c;如果车辆的惯性动能大于轮胎抓地力&#xff0…
最新文章