关于线索二叉树的详解

2023-11-02

线索二叉树的详解


前言

学了二叉树,我们发现,对二叉树的遍历是一个比较复杂的问题,需要用到递归或者栈才可以进行遍历,这样子的遍历实质上就是将二叉树化为一个有序的线性序列,在这个序列中,每一个节点有且只有一个前继节点(第一个节点除外),和一个后继节点(最后一个节点除外),但是这些节点不能直接的找到当前节点的前驱和后继信息,与此同时,二叉树在存储的过程中,会有很多的空节点,为了充分利用这些节点以及遍历更加方便,我们产生了线索二叉树


一、线索二叉树是什么?

为了区分二叉树的左孩子指针和右孩子指针是否为空,或者是否指向前驱节点或后继节点,我们将节点的结构改成5个域,在原二叉树的基础上添加左标志域Ltag和右标志域Rtag,他们是两个int型的数据域。

Lchild Ltag data Rtag Rchild
  1. 如果节点有左孩子,那么Lchild依然指向他的左孩子,否则指向遍历序列中他的前驱节点。

  2. 如果节点有右孩子,那么Rchild依然指向他的左孩子,否则指向遍历序列中他的后继节点。

  3. LtagRtag的定义如下:
    Ltag : 等于0时,Lchild域指示节点的左孩子;等于1时,Lchild指示节点的遍历前驱。
    Rtag : 等于0时,Rchild域指示节点的左孩子;等于1时,Rchild指示节点的遍历后继。

一些小概念
线索:指向前驱和后继节点的指针。
线索化:将空指针改为线索的过程。

三种二叉树线索化实例图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、实现线索二叉树

以下都是以中序的方式进行的,先序和后序只需进行小小的改动即可

1.二叉树的线索化

    首先我们会遇到的问题就是,当遇到空指针的时候我们应该如何填写相应的内容,可分为两种情况讨论。
    当遍历到左孩子指针域为空的时候,我们应该填入该节点的遍历前驱节点的指针,这时候我们可以提前声明一个全局变量pre来记录刚刚访问过的节点,在遍历过程中如果遇到左孩子为空的节点,就将pre赋给他的左孩子域,并且将Ltag设置为1。pre的初值应该为NULL,因为便利的首节点的前驱节点一定为空。
    当遍历过程中右孩子的指针域为空的时候,要填的节点应该为当前节点遍历序列的后继节点,但是我们现在无法确定,因此当前节点的右孩子指针域只能遍历到下一个节点的时候在填,并且当前节点就是pre节点的后继节点,所以我们只需要在遍历的过程中加上判断,回填pre的语句,若pre的右孩子节点为空,那么将当前的节点赋值给pre的右节点,同时pre节点的Rtag应该置为1

中序线索化代码如下(示例):

void Inthread(BiTree root) 
{
//pre为全局变量,在函数外已经声明过
	if(root != NULL)
	{
		Inthread(root->Lchild);//递归线索化root的左孩子
		if(root->Lchild == NULL) //置前置线索 
		{
			root->Lchild = pre;
			root->Ltag = 1;
		}
		else{
			root->Ltag = 0;
		}
		if(pre != NULL && pre->Rchlid == NULL)//置后置线索
		{
			pre->Rchlid = root;
			pre->Rtag = 1;
		} 
		pre = root;
		Inthread(root->Rchlid);//递归线索化root的右孩子
	}
}

2.线索二叉树的遍历

    二叉树的中序遍历的思路就是:首先访问左子树,在访问根节点,最后访问右子树。

中序线索二叉树寻找遍历的首节点

    按照这样的访问次序,首先访问的是树的最左下端,即沿着左孩子链走到最下端,找到第一个没有左孩子的节点(Ltag == 1)
代码如下(示例):

BiTree InFirst(BiTree bt)
{
	BiTree p = bt;
	if(p == NULL)
	return(NULL);
	//因为是中序遍历,所以最开始的节点一定是最左边的 
	while(p->Ltag == 0)
	
		p = p->Lchild;
	
		
	return(p);
}

中序线索二叉树寻找节点的直接后继

    对于传进来的参数节点p,如果p没有右孩子那么直接可以获得他的后继节点,如果p有右子树,那么他的后继节点应该是p右子树中第一个遍历到的节点,有中序遍历我们可以知道,p的后继节点就是他右子树的最左下端第一个没有左孩子的节点
代码如下(示例):

BiTree InNext(BiTree p) 
{
	BiTree next,q;
	if(p->Rtag == 1) 
	{
		next = p->Rchlid;//直接利用线索	 
	}
	//右边的子树查找后继节点有问题 
	else{
		//在p的右子树中查找最左下端的节点 
		for(q = p->Rchlid; q->Ltag  == 0; q = q->Lchild);
		next = q;
		
	} 
	return(next);
}

遍历中序线索二叉树

    最后利用上述的InFirst和InNext函数进行遍历
代码如下(示例):

BiTree TinOrder(BiTree root) 
{
	BiTree p;
	p = InFirst(root);
	while(p != NULL) 
	{
		printf("%c",p->data);
		p = InNext(p);
	}
}

总结

通过上述的方法我们可以不用递归和栈从而实现对二叉树的遍历

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于线索二叉树的详解 的相关文章

随机推荐

  • var模型matlab代码_VAR模型

    前言 说来话长 这是失败的实践 前几天有个比赛 其中数据处理部分 它给出了前很多年2G 3G 4G 总无线接入网络数据规模的数据 让预测2020年5G和总的 当时一看题就觉得要用时间序列模型 多元时间序列模型就想拿VAR练练手 但是因果检验
  • 接口测试工具Apifox 基础篇:数据传递与处理

    一 接口之间如何传递数据 1 使用场景 接口B请求参数依赖于接口A返回的数据 2 实现思路 2 1 接口A使用后置操作 gt 提取变量功能将请求完成后返回的数据提取作为变量 2 2 接口B对应的参数值直接引用前面设置的变量 3 使用示例 3
  • R语言学习:数据结构2-向量

    向量 vector 只能包含同一类型的对象 创建向量 向量的类型 命名 vector x lt vector character length 10 生成指定长度的空向量 x1 lt 1 4 x2 lt c 1 2 3 x3 lt c TR
  • Apple iOS MDM开发流程

    一年前曾参与一个企业移动平台项目 实现了通过MDM对iOS设备进行管理 由于苹果对于mdm这块的接口及开发流程只向几个合作伙伴进行了分享 并没有对具体实现的文档进行公开 所以这方面的资料非常少 现在把实现的过程分享给大家 希望能对大家有所帮
  • pandas常用功能与函数介绍(结合实例,持续更新)

    本文首先介绍Pandas常用功能及函数 最后通过实例举例说明 一 常用功能及函数简介 包导入 一般我们需要做如下导入 numpy和pandas一般需要联合使用 import pandas as pd import numpy as np 本
  • meethigher-JPA实体监听器-@EntityListeners

    参考文章JPA实体类监听器 EntityListeners注解使用实例 疯狂的蜗牛 CSDN博客 entitylisteners 本文源码 这也是来源于工作中的一个小需求 因为产品迭代时 需要给前端创建人 但是由于创建人是在操作记录的表里记
  • 【linux系统基础知识-Shell脚本学习笔记12-循环】

    12 1 循环说明解释 循环是可以使你多次执行一系列命令 循环包括 for循环 while循环 select循环 for do done while do done until do done 12 2 for循环 for循环使你对列表中的
  • 用TW8836驱动ST7701S TTL屏调试记录

    近段时间做一个项目 要调试3 2寸320x820分辨率的LCD 在此做下记录 屏规格书如上图 屏的主要接口如上图 1 查看屏的规格书 如图所示 需要8836和st7701s通讯 方式是3线SPI 2 通讯接口SDA SCK CS 3 RGB
  • zabbix搭建

    1 环境 本实验使用一台centos7主机 关闭了firewalld和selinux服务 zabbix版本为5 0版本 mysql使用版本为5 7版本 若要搭建6 0以上版本的zabbix 则需要使用mysql 8 0以上的版本 其它版本的
  • 微信公众号获取用户的openid

    公众号可获得关注者的OpenID 加密后的微信号 每个用户对每个公众号的OpenID是唯一的 对于不同公众号 同一用户的openid不同 公众号可通过本接口来根据OpenID获取用户基本信息 包括昵称 头像 性别 所在城市 语言和关注时间
  • shell 脚本中 $$、$#、$? 分别代表什么意思?

    0 这个程式的执行名字 n 这个程式的第 n 个参数值 n 1 9 这个程式的所有参数 此选项参数可超过 9 个 这个程式的参数个数 这个程式的 PID 脚本运行的当前进程 ID 号 执行上一个背景指令的 PID 后台运行的最后一个进程的进
  • Ubuntu16.04 ARM/Qt 交叉编译环境搭建

    嵌入式开发 Ubuntu16 04 ARM Qt 交叉编译环境搭建 背景 环境说明 安装交叉编译工具 下载Qt源码包 编译Qt源码 安装QtCreator 配置QtCreator 应用QtCreator交叉编译 Ubuntu16 04 AR
  • springboot配置双mysql数据源

    这两天一直在配置双数据源 找了网上很多资料 有的资料写的太乱而且注释不清楚 类不全 像我这样的刚开始配置的新手很难看明白 今天终于配置成功了 我把我总结的整理一下 做个日志以防以后遇到问题 一 创建一个springboot项目其中需要的po
  • h5页面判断 js判断 是否安装APP,如果安装就拉起APP 打开app ,否则就下载

    h5页面判断是否安装APP 如果安装就拉起APP 否则就下载 if navigator userAgent match iPhone iPod iPad i var loadDateTime new Date window location
  • PHP[多维数组转字符串]和{多维数组转一维数组}

    http blog csdn net aoyoo111 article details 8554585 php view plain copy print method 多维数组转字符串 param type array return ty
  • 基于Echarts4.0实现旭日图

    昨天Echarts4 0正式发布 随着4 0而来的是一系列的更新 挑几个主要的简单说明 1 展示方面通过增量渲染技术 4 0 ECharts 能够展现千万级的数据量 2 针对移动端优化 移动端小屏上适于用手指在坐标系中进行缩放 平移 可选的
  • 探索AIDL(1) -- 初识AIDL

    探索AIDL 1 初识AIDL 前言 1 在讨论这个问题之前必须先理解IPC的概念 IPC全称是Inner Process Communication 即跨进程通信 是指两个进程进行数据交换的过程 2 如果要传递对象必须先进行序列化 序列化
  • RV1126 SDK编译错误及解决记录

    RV1126 SDK编译错误及解决记录 1 错误 you need to install unbuffer from package expect or expect dev log saved on home h00003 RV1126
  • Win11怎么设置开机启动项?

    我们在使用电脑的时候经常会打开非常多的软件 而每次开机都需要手动去点击 就会变得非常的麻烦 那么在Win11操作系统中我们应该怎么设置呢 其实方法非常简单 下面小编就带着大家一起看看吧 操作方法 方法一 1 首先 按键盘上的 Win 键 或
  • 关于线索二叉树的详解

    线索二叉树的详解 目录 线索二叉树的详解 前言 一 线索二叉树是什么 三种二叉树线索化实例图 二 实现线索二叉树 1 二叉树的线索化 2 线索二叉树的遍历 中序线索二叉树寻找遍历的首节点 中序线索二叉树寻找节点的直接后继 遍历中序线索二叉树