数据结构与算法----详解二叉树的遍历(迭代、递归)

news2024/11/25 14:26:42

文章目录

    • 实现二叉树的类
    • 前序遍历
    • 中序遍历
    • 后序遍历
    • 层次遍历
    • 总结

  • ❤️ 作者简介:大家好我是小鱼干儿♛是一个热爱编程、热爱算法的大三学生,蓝桥杯国赛二等奖获得者
  • 🐟 个人主页 :https://blog.csdn.net/qq_52007481
  • 个人社区:【小鱼干爱编程】
  • 🔥 算法专栏:算法竞赛进阶指南
  • 💯 刷题网站:虽然市面上有很多的刷题网站,但是里面的题又多又杂,不适合系统性的提高算法能力,如何挑选一个适合自己的刷题网站呢,这里推荐一款我常用的刷题网站 👉牛客网

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分。--------百度百科

在这里插入图片描述

二叉树的遍历是使用二叉树的最基础的操作,常见的几种遍历方式有,前序遍历,中序遍历,后续遍历,层次遍历,这里所说的前、中、后是表示父节点被访问的次序,层次节点是表示一层一层,从左往右访问节点

实现二叉树的类

因为这里是讲解的二叉树的几种遍历形式,二叉树添加的数据的部分就不再写了,
结果测试我们就去刷题网站测试:牛客网

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

每种遍历都有两种方式递归、迭代,因为递归不仅容易出现超过最大递归深度,而且非常浪费计算机资源,因此在实际使用过程中我们应该尽量的避免递归,在大多数情况下递归都有对应的迭代版本。

在实现二叉树的迭代遍历的过程中,使用了栈的思想,我们会用一个栈来存储节点的位置

前序遍历

前序遍历:父节点 > 左节点 > 右节点

在这里插入图片描述

  • 递归方式
arr = []
def preOrder(root):
    if root==None:    # 函数停止的条件,
        return 
    arr.append(root.val)   # 先存放父节点的元素
    preOrder(root.left)    # 将左节点当作一个树,继续执行 
    
    preOrder(root.right)   # 将右节点当作一个树,继续执行 
  • 迭代方式

栈的作用就是存储还未被遍历节点的指针,等后面使用的时候在取出
工作流程:

  • 3进栈,2进栈、输出1的值,2出栈
  • 5进栈,4进栈、输出2的值,4出栈
  • 7进栈,输出4的值,7出栈
  • 输出7的值,3出栈
  • 6进栈,输出3的值,6出栈
  • 8进栈,输出6的值,8出栈
  • 输出8的值
stack = []   # 栈,用来存储节点的指针
while root:  # 当二叉树存在时执行循环
    if root.right !=None:  # 因为是先序遍历,所有右边的节点要先进栈
        stack.append(root.right)
    if root.left!=None:
        stack.append(root.left)
    print(root.val)       #  每循环一个节点就输出一个值,可以根据需要调整
    if len(stack) == 0:
        break
    else:
        root = stack.pop()   # 弹出栈顶的元素 

中序遍历

中序遍历:中节点 > 父节点 > 右节点

在这里插入图片描述

  • 递归方式
arr = []
def medOrder(root):
    if root==None:
        return 
    medOrder(root.left)     # 先找到最左叶子节点,
    arr.append(root.val)    # 
    medOrder(root.right)
  • 迭代方式
    先找到最左节点,从最左节点开始遍历二叉树

工作流程:

  • 1进栈
  • 2进栈
  • 4进栈
  • 7进栈
  • 7出栈 输出7的值
  • 4出栈 输出4的值
  • 2出栈,输出2的值,
  • 5进栈
  • 5出栈,输出5的值
  • 1出栈,输出1的值
  • 3进栈
  • 3出栈,输出3的值
  • 6进栈
  • 8进栈,输出8的值
  • 6出栈,输出6的值
stack = []
while True:
    if root:
        stack.append(root)  # 将节点存入栈中
        root = root.left
    elif len(stack)>0:
        root = stack.pop()  # 取出栈顶的元素,第一次取到的是最左节点,
        print(root.val)
        root = root.right   
    else:
        break

后序遍历

后序遍历:左节点 > 右节点 > 父节点

在这里插入图片描述

  • 递归方式
arr = []
def postOrder(root):
    if root==None:
        return 
    postOrder(root.left)
    postOrder(root.right)
    arr.append(root.val)
  • 迭代方式
    因为父节点在左右节点的之间,所以用迭代的方式实现后续遍历,比较困难,需要一个额外的数组用来存放左右两边都遍历过的父节点。
  • 1进栈
  • 2进栈
  • 4进栈
  • 7进栈
  • 7出栈,输出7的值
  • 4出栈,输出4的值
  • 2出栈,左节点还未被遍历,2重新进栈
  • 5进栈
  • 5出栈, 输出5的值
  • 2出栈,输出2的值
  • 1出栈,左节点还未被遍历,1重新进栈
  • 3进栈
  • 6进栈
  • 8进栈
  • 8出栈,输出8的值
  • 6出栈,输出6的值
  • 3出栈,输出3的值
  • 1出栈,输出1的值
stack = []  # 栈
lst = []  # 存放左右两边都被遍历过父节点
while True:
    if root:
        stack.append(root)   # 将节点入栈
        root = root.left     # 将左节点当作新的二叉树
    elif len(stack)>0:
        root = stack.pop()    # 弹出栈顶的元素
        if root.right:   # 查看该节点是否有右节点,如果有判断是否被遍历过
            if root in lst:  # 该节点的左右两边都被遍历
                lst.remove(root)  # 从该数组中删除,(不删除也可以,删除会减少内存空间)
                print(root.val)   # 输出该值
                root = None       # 因为都被访问过了,就类似一个空树
            else:   # 该节点的右节点还未被遍历
                stack.append(root)  # 重新将该节点入栈
                lst.append(root)    # 其实此时可能并没有遍历,但是因为栈的特性,我们可以知道遍历右节点一定早于父节点所以可以添加到左右两边都被遍历的数组中
                root = root.right   # 将右节点当作一个新的二叉树,继续循环               
        else:
            print(root.val)
            root = None      # 当没有右节点,则代表树为空
    else:
        break

层次遍历

在这里插入图片描述
层次遍历只有迭代版本
层次遍历使用的就不是栈, 使用的是队列(先进先出)

算法执行的流程

  • 1入队
  • 1出队,输出1的值,2入队,3入队
  • 2出队,输出2的值,4入队,5入队
  • 3出队,输出3的值,6入队
  • 4出队,输出4的值,7入队
  • 5出队,输出5的值
  • 6出队,输出6的值,8
  • 7出队,输出7的值
  • 8出队,输出8的值
queue = []
if root == None:
    return []
queue.append(root)
while len(queue)>0:
    root = queue.pop(0)
    print(root.val)
    if root.left != None:
        queue.append(root.left)
    if root.right != None:
        queue.append(root.right)

练习题:

👉直达牛客,快人一步

在这里插入图片描述
题解1,使用递归解题

class Solution:
    def threeOrders(self , root: TreeNode) -> List[List[int]]:
        # write code here
        # 先序遍历
        arr = [[],[],[]]
        def preOrder(root):
            if root==None:
                return
            arr[0].append(root.val)
            preOrder(root.left)
            preOrder(root.right)
        preOrder(root)
        def medOrder(root):
            if root==None:
                return
            medOrder(root.left)
            arr[1].append(root.val)
            medOrder(root.right)
        medOrder(root)
        def postOrder(root):
            if root==None:
                return
            postOrder(root.left)
            postOrder(root.right)
            arr[2].append(root.val)
        postOrder(root)
        return arr

题解2,简化递归,
从第一个我们能够发现,前序后续的算法非常相似,只是顺序有区别,我们其实可以将这些同时放在一个函数里面。

class Solution:
    def threeOrders(self , root: TreeNode) -> List[List[int]]:
        arr = [[],[],[]]
        def Order(root):
            if root==None:
                return
            arr[0].append(root.val)
            Order(root.left)
            arr[1].append(root.val)
            Order(root.right)
            arr[2].append(root.val)
        Order(root)
        return arr

题解3 使用迭代
使用迭代的,虽然看着代码非常复杂,但是随着数据量的增多,效率会比迭代的越来越强。


class Solution:
    def threeOrders(self , root: TreeNode) -> List[List[int]]:
        # write code here
        # 先序遍历
        base = root
        arr = [[],[],[]]
        stack = []
        while root:
            if root.right !=None:
                stack.append(root.right)
            if root.left!=None:
                stack.append(root.left)
            arr[0].append(root.val)
            if len(stack) == 0:
                break
            else:
                root = stack.pop(len(stack)-1)
                
        root = base
        stack = []
        while True:
            if root:
                stack.append(root)
                root = root.left
     
            elif len(stack)>0:
                root = stack.pop()
                arr[1].append(root.val)
                 
                root = root.right
            else:
                break
 
        root = base
        stack = []
        lst = []
        while True:
            if root:
                stack.append(root)
                root = root.left
            elif len(stack)>0:
                root = stack.pop()
                if root.right:
                    if root in lst:
                        lst.remove(root)
                        arr[2].append(root.val)
                        root = None
                    else:
                        stack.append(root)
                        lst.append(root)
                        root = root.right
                         
                else:
                    # 取值
                    arr[2].append(root.val)
                    root = None
            else:
                break
        return arr

总结

二叉树遍历的遍历,递归的函数比较容易写,但是太浪费计算机的资源,所以要改写为迭代的方式实现。我们在使用迭代实现的时候,应用了栈的思想,层次遍历使用了队列的思想

补充:因为这篇文章是需要有一定的基础的,大家如果对栈和队列不太明白的可以看我这篇文章数据结构与算法----栈和队列(Stack & Queue)
如果文章有哪些问题也欢迎大家大家指正

在这里插入图片描述

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

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

相关文章

微信小程序跳转微信内置浏览器

最近遇到一个需求,需要微信小程序跳转到微信内置浏览器,但是官网并没有给出相关文档。查阅了大量资料,发现有两种实现方式。 1、通过微信公众号文章实现 小程序可以使用web-view打开关联的公众号文章,公众号文章可以在阅读全文放…

macOS Ventura 13.3 (22E252) 正式版 ISO、DMG镜像下载

macOS Ventura 版本历史 Release: macOS Ventura 13.3 (22E252), 2023-03-27macOS Ventura 13.2.1 (22D68), 2023-02-18macOS Ventura 13.2 (22D49), 2023-01-23macOS Ventura 13.1 (22C65), 2022-11-13macOS Ventura 13.0.1 (22A400), 2022-11-09macOS Ventura 13.0 (22A380…

关于结构体初始化的自我小结

前段时间在一个项目中使用到结构体数组来存储产品不同型号的参数,使程序通用化,便于测试和快速生产。由于之前很少使用结构体数组,在初始化时遇到了一点小阻碍,于是便想到对于结构体和其数组的初始化操作做一个小总结:…

前端笔试常考设计模式,操作系统,数据结构,ACM模板,经典算法,正则表达式,常用方法

考试时允许使用草稿纸,请提前准备纸笔。考试过程中允许上厕所等短暂离开,但请控制离开时间 笔试得分60%一般通过,面试答对80%才能通过 合集:2023年最全前端面试题考点HTML5CSS3JSVue3React18八股文手写项目笔试_参宿7的博客-CSD…

Microsoft Visual Studio 2019正式版离线安装包下载

原文地址:https://www.bitecho.net/microsoft-visual-studio-2019.html#respond Visual Studio(简称VS)是微软公司的开发工具包系列产品,包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开…

DIY信号发生器:运放实现三角波、方波发生器(详细参数说明)+multisim仿真

前言 信号发生器是电子工程师最常用的几个仪器之一吧,三角波和方波是最常用的波形,在之前的文章中,我们已经介绍过RC延迟电路,今天我就教大家通过RC延迟和运放来实现三角波和方波。 仿真软件版本 本次介绍的电路是通过multisim…

kafka如何保证消息不丢失?半分钟的答案和半个小时的答案有点不一样。

文章目录 一、Kafka在哪些场景下有丢消息的可能?二、面试流经典答法三、为什么金融场景没人会用Kafka?总结 ​ kafka如何保证消息不丢失? 这是面试最常问到的问题。但是其实这是一个最体现综合实力的开放性题目。把这问题真正弄明白&#xf…

uni - app

uni-app 使用vue的语法&#xff0c;vue指令 小程序的标签和 API <template><view class"content"><image class"logo" src"/static/logo.png"></image><view class"text-area"><text class"t…

期末作业C#实现学生宿舍管理系统

??开发背景 完整代码下载地址&#xff1a;点我下载 优化移步&#xff1a; 《c#中在datagridview的表格动态增加一个按钮方法》 《C#实现多窗口切换&#xff1a;Panel详细教程&#xff08;亲测&#xff09;》 文章还在更新&#xff0c;上次更新时间2022/06/20 20:49 由于快期…

unapp微信小程序转发分享、携带参数

一、第一种方式&#xff1a; // 开启小程序原生右上角分享按钮uni.showShareMenu({// https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share.htmlwithShareTicket: true,menus: [shareAppMessage, shareTimeline]//开启转发好友和转发朋友圈按钮});…

最详细python安装库的方法(以安装pygame库为例)

文章目录 前言 &#x1f680;&#x1f680; python安装库方法大全&#xff08;以安装pygame库为例&#xff09; &#x1f680;方法一、在pycharm内部直接安装【最简单的方法&#xff08;直接上图&#xff09;】 &#x1f680;方法二、&#xff08;在终端安装库&#xff09;…

Unity碰撞检测的必要条件

Unity中有两个独立的物理引擎&#xff0c;一个用于3D物理系统&#xff0c;一个用于2D物理系统。两个引擎是使用不同的组件实现的。因此BoxCollider和Rigidbody一起使用&#xff0c;代码中用OnTriggerEnter才能检测到触发;BoxCollider2D和Rigidbody2D一起使用&#xff0c;代码中…

(附源码)基于springboot考试系统 毕业设计 191015

基于springboot考试系统 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对在线考试等问题…

49天精通Java,第29天,Java内部类、java内部类的作用

目录 一、为什么需要内部类?二、内部类分为四种三、成员内部类1、什么是成员内部类2、代码实例3、成员内部类进阶代码实例4、控制台显示5、外部类访问内部类四、局部内部类五、匿名内部类1、匿名内部类简介2、代码实例3、控制台输出4、访问权限六、静态内部类七、内部类的使用…

【进阶C语言】内存函数(详解)

前言 上一期讲的函数都是和字符串相关的&#xff0c;但是我们在操作数据的时候&#xff0c;不仅仅是操作字符串的数据&#xff0c;还得需要内存函数的应用 内存函数的应用 1. memcpy1.1 memcpy的介绍1.2 memcpy的使用1.3 模拟实现memcpy库函数1.4 我想在1&#xff0c;2后面打印…

Ubuntu开启SSH免密登录

Ubuntu开启SSH免密登录 要实现SSH免密登录&#xff0c;首先需要准备一组公钥和私钥。将公钥放到服务器上&#xff0c;将私钥放到客户机上。当客户机连接服务器时&#xff0c;服务器会根据自身的公钥校验客户机的私钥&#xff0c;如果校验通过则允许连接。 一、创建密钥 在客…

(Linux)Centos7.*版本安装配置Java环境、Tomcat、Nginx并打包部署SSM框架web系统

目录 一、准备软件与安装包 (一)、必须的软件 1、点击下载Xshell 2、点击下载FileZilla (二)、准备安装包 1、点击下载JDK1.8Linux版本 2、点击下载Nginx 3、点击下载Tomcat 二、关于FileZilla软件的使用说明 (一)、FileZilla软件的打开和说明 (二)、配置服务器地…

什么是FIFO?

同步FIFO和异步FIFO 1、FIFO定义 FIFO是英文First In First Out的缩写&#xff0c;是一种先进先出的数据缓存器&#xff0c;他与普通存储器的区别是没有外部读写地址线&#xff0c;这样使用起来非常简单&#xff0c;但缺点就是只能顺序写入数据&#xff0c;顺序的读出数据&am…

C++修炼之筑基期第三层——拷贝构造函数

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f337;拷贝构造函数的概念&#x1f337;拷贝构造函数的特性 &#x1f490;专栏导读 &#x1f338;作者简介&#xff1a;花想云&#xff0c;在读本科生一枚&#xff0c;致力于 C/C、Linux 学习。 &#x1f338;本文…

Rocky9/Centos stream9 修改静态ip,修改网卡。

目录 需求&#xff1a; 修改ipv4地址为10.10.10.10 子网掩码为255.255.255.0 网关为10.10.10.254 dns为本机ip 当前版本&#xff1a; 前言&#xff1a; 正文&#xff1a; 后续其他方法拓展。 本人新建立一个QQ shell群&#xff0c;感兴趣的可以加入&#xff1a;637257233 …