数据结构+算法(第13篇):精通二叉树的“独门忍术”——线索二叉树(上)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

引言

二叉树的叶子节点的孩子都是空节点(Null),如果展开显示,如下图:

图 1 原始二叉树

二叉树的遍历方法,有“前序遍历”“中序遍历”和“后序遍历”三种。

“前序遍历”的规则:

  1. 先访问当前节点,再访问其左子树,最后访问右子树;
  2. 访问子树时,按照规则1递归执行。

“中序遍历”的规则:

  1. 先访问左子树,再访问当前节点,最后访问右子树;
  2. 访问子树时,按照规则1递归执行。

“后序遍历”的规则:

  1. 先访问左子树、再访问右子树,最后访问当前节点;
  2. 访问子树时,按照规则1递归执行。

如果要写出非递归的遍历算法,无论用哪种遍历方法,根据《再不会“降维打击”你就Out了!》《神力加身!动态编程》三篇文章中讲到的知识和技巧,都要借助堆栈来记忆“历史路径”以用于回溯。此方法是经典做法,但同时也有两个显著弊端:

  1. 堆栈需要额外的存储;
  2. 额外需要的存储带来的空间复杂度也不是O(1)型的——是与节点总数动态相关。

那么是否存在能找到一种技巧来解决上述的弊端呢?

今天就来介绍一种“奇技淫巧”——线索二叉树——来搞定这个问题:)

什么是线索二叉树?

严格意义上的线索二叉树定义如下:

一个二叉树通过如下的方法“穿起来”:所有原本为空的右(孩子)指针改为指向该节点在中序序列中的后继,所有原本为空的左(孩子)指针改为指向该节点的中序序列的前驱。

本文为了追求更直观、更快速的算法效果,对上述传统线索二叉树做了如下改良:

  1. 不同的遍历方法,对应的线索二叉树不同;
  2. 尽可能只利用后继节点。

这里先以“中序遍历”对应的线索二叉树为例。

“中序遍历”的线索二叉树说白了,就是两条规则:

  1. 将当前节点左子树中的最右叶子节点的右孩子指针指向当前节点本身;
  2. 对每个节点,递归执行规则1。

规则1看起来比较绕,用一张图来表示:

图2 “中序遍历”的线索二叉树

图2就是图1对应的“中序遍历”的线索二叉树。

线索二叉树的意义

传统的非递归型遍历算法,最挑战的地方在于要记忆“回溯点”。

以“中序遍历”为例,它要先访问当前节点的左子树之后,再访问当前节点——这意味着,访问完左子树前,先要记住当前节点位置;否则,访问完左子树之后,就找不到返回位置了。经典做法是通过堆栈来记忆。如果不想引入额外存储,那么怎么“记住”呢?

对比图2和图1,可以看出:“中序遍历”的线索二叉树其实就是复用了指向“空节点”的指针!——它将当前节点左子树的最右叶子节点的右孩子指针(原来指向“空节点”),指向了当前节点!

至于“前序遍历”的线索二叉树,就是将当前节点左子树的最右叶子节点的右孩子指针(原来指向“空节点”),指向当前节点的直接右孩子:

图3 “前序遍历”的线索二叉树

至于“后序遍历”的线索二叉树,就复杂了:

  1. 仅仅利用叶子节点的指针,解决不了所有后继节点的寻址;
  2. 非叶子节点的孩子指针无法直接复用,若用于指示后继节点,会丢失本来的孩子节点的链接。

图4 “后序遍历”的线索二叉树构造问题

解决上述困难,有两种途径:

  1. 利用其它遍历方法的线索二叉树来做“后序遍历”;
  2. 对原始二叉树做结构改造,以符合前驱或者后继寻址的需要。

如何将二叉树转换成线索二叉树?

为了节省篇幅,本文仅介绍“中序遍历”的线索二叉树的转换以及遍历算法。

构造线索二叉树的目的,说到底还是为了遍历。那么这就引出一个现实问题:

到底是构造完线索二叉树之后,再启动遍历呢?还是边构造边遍历呢?

从上面的线索二叉树的定义就可以看出,为了复用“空节点”指针,需要访问叶子节点,这个已经是遍历的一部分了,所以为了“不走重复路”,最经济的方法是边构造边遍历。

递归型“中序遍历”的线索二叉树的转换以及遍历算法:

非递归型“中序遍历”的线索二叉树的转换以及遍历算法:

后记:

接下来的文章将分别介绍“前序遍历”的线索二叉树的各种转换以及遍历算法、“后序遍历”的线索二叉树的各种转换以及遍历算法。

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

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

相关文章

【数据结构】(分治策略)中位数的查询和最接近点对问题

中位数查询: 寻找一组字符串中第k小的数,返回其值和下标。 不可以有重复值(在缩小规模的时候,会导致程序死循环) 相对位置的转换体现了分治策略的思想。> 划分函数 int partition(int *nums,int left, int rig…

BUUCTF-Real-[Flask]SSTI

目录 漏洞描述 模板注入漏洞如何产生? 漏洞检测 漏洞利用 get flag ​编辑 漏洞描述 Flask框架(jinja2)服务端模板注入漏洞分析(SSTI) Flask 是一个 web 框架。也就是说 Flask 为您提供工具、库和技术来允许您构…

浅谈WPF之UniformGrid和ItemsControl

在日常开发中,有些布局非常具有规律性,比如相同的列宽,行高,均匀的排列等,为了简化开发,WPF提供了UniformGrid布局和ItemsControl容器,本文以一个简单的小例子,简述,如何…

[Java]JDK 安装后运行环境的配置

这篇文章用于介绍jdk.exe安装之后的运行环境配置,以及如何检查是否安装成功 检查自己是否安装jdk环境,记住这个安装的改的路径: (应该要安装2个,一个是jdk,一个是jre) 安装后的在文件夹的样子(路径自定义,在java下面): 参考如下…

奠定基础:用于机器学习的微积分、数学和线性代数

一、说明 机器学习是一个引人入胜的领域,它使计算机能够从数据中学习并做出预测或决策,而无需明确编程。然而,在幕后,有一个坚实的数学和线性代数基础,构成了机器学习算法的支柱。在本文中,我们将探讨在深入…

AJAX-URL查询参数

定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据 http://xxxx.com/xxx/xxx?参数名1值1&参数名2值2 axios语法 使用axios提供的params选项 注意:axios在运行时把参数名和值,会拼接到url?参数名值 axios(…

YOLOv7改进:下采样系列 | 一种新颖的基于 Haar 小波的下采样HWD,有效涨点系列

💡💡💡本文独家改进:HWD的核心思想是应用Haar小波变换来降低特征图的空间分辨率,同时保留尽可能多的信息,与传统的下采样方法相比,有效降低信息不确定性。 💡💡💡使用方法:代替原始网络的conv,下采样过程中尽可能包括更多信息,从而提升检测精度。 收录 YO…

用Python和 Cryptography库给你的文件加密解密

用Python和 Cryptography库给你的文件加密解密 用Python和 Cryptography库给你的文件加把安全锁。 先介绍与加密解密有关的几个基本概念。 加密(Encryption):加密是将明文转换为密文的过程,使得未经授权的人无法读懂。 解密&a…

Ruoyi-Cloud-Plus_Nacos配置服务漏洞CVE-2021-29441_官方解决方法以及_修改源码解决---SpringCloud工作笔记199

CVE-2021-29441 这个漏洞是Nacos的,通过使用postman,直接访问接口: 就可以直接添加nacos的用户 Nacos是Alibaba的一个动态服务发现、配置和服务管理平台。攻击者通过添加Nacos-Server的User-Agent头部将可绕过(nacos.core.auth.enabled=true)鉴权认证,从而进行API操作。 …

spring boot 使用 Kafka

一、Kafka作为消息队列的好处 高吞吐量:Kafka能够处理大规模的数据流,并支持高吞吐量的消息传输。 持久性:Kafka将消息持久化到磁盘上,保证了消息不会因为系统故障而丢失。 分布式:Kafka是一个分布式系统&#xff0c…

海康威视有插件、无插件播放;webrtc直播;西瓜视频播放器;mpegts.js直播;flvjs直播

Notes 视频播放的几种方式 一、Video mp4链接直接播放 二、海康威视3.3插件版直播、云台控制,资源下载地址 index.html引入hk文件中的js文件双击HCWebSDKPlugin.exe安装插件前端参照文件夹hkCamera中的示例代码 三、海康威视3.2无插件版直播,资源下…

go_view同后端集成时的注意事项

goview是一个不错的可视化大屏配置工具;提供了丰富的功能可供调用。 官方地址和文档: https://gitee.com/dromara/go-view https://www.mtruning.club/guide/start/ 同nodejs集成可参考;https://gitee.com/qwdingyu/led (建议–后端集成有api功能,可直接配置sql)同dotne…

Leetcode—203. 移除链表元素【简单】

2024每日刷题(一零九) Leetcode—203. 移除链表元素 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(n…

Python爬虫的基本原理

我们可以把互联网比作一张大网,而爬虫(即网络爬虫)便是在网上爬行的蜘蛛。把网的节点比作一个个网页,爬虫爬到这就相当于访问了该页面,获取了其信息。可以把节点间的连线比作网页与网页之间的链接关系,这样…

ref和reactive

看尤雨溪说:为什么Vue3 中应该使用 Ref 而不是 Reactive?

华为1.24秋招笔试题

华为1.24秋招笔试题 1.题目1 题目详情 - 2024.1.24-华为秋招笔试-第一题-计算积分 - CodeFun2000 1.1题解 import java.util.Scanner;class Main{public static void main(String[] args){Scanner scnew Scanner(System.in);String ssc.next();char[] chs.toCharArray();in…

记一次 Android CPU高使用率排查

文章目录 背景排查高占用的进程adb shelltoptop -b -H -n 1 | grep 29337 (打印各线程 cpu使用详情)kill -3 29337 (生成trace文件)adb pull /data/anr /Users/gerry.liang/Desktop定位问题 补充说明: 背景 测试同学反馈我们的App CPU使用率 90% 居高不下,经过一番艰难的排查后…

如何在PS5上使用金手指修改游戏

环境:windows PS5 问题:PS5 没有GodHen,无法使用json金手指,PKG金手指比较少 解决办法:使用MultiTrainerv从网络注入PS5,修改进程内存 背景:为了护肝,拒绝刷刷刷 解决过程&#xff…

网络异常案例四_IP异常

问题现象 终端设备离线,现场根据设备ip,ping不通。查看路由器。 同一个路由器显示的终端设备(走同一个wifi模块接入),包含不同网段的ip。 现场是基于三层的无线漫游,多个路由器wifi配置了相同的ssid信息&a…

VUE项目导出excel

导出excel主要可分为以下两种: 1. 后端主导实现 流程:前端调用到导出excel接口 -> 后端返回excel文件流 -> 浏览器会识别并自动下载 场景:大部分场景都有后端来做 2. 前端主导实现 流程:前端获取要导出的数据 -> 把常规数…