线索二叉树(前中后序线索化/遍历/画线索)

news2024/11/25 2:33:19

线索二叉树

文章目录

  • 线索二叉树
    • 1 线索二叉树的基本概念
    • 2 线索二叉树的构造
      • 2.1 线索二叉树的存储结构
      • 2.2 给线索二叉树画线索
        • 2.2.1 中序
        • 2.2.2 先序
        • 2.2.3 后序
      • 2.3 二叉树线索化代码实现
        • 2.3.1 通过中序遍历线索化
        • 2.3.2 通过先序遍历线索化
        • 2.3.3 通过后序遍历线索化
    • 3 线索二叉树的遍历
      • 3.1 中序线索二叉树的遍历
      • 3.2 先序线索二叉树的遍历
      • 3.3 后序线索二叉树的遍历
    • 4 补充知识点

1 线索二叉树的基本概念

对一棵二叉树中所有结点的空指针域按照某种遍历方式加线索的过程叫作线索化,被线索化了的二叉树称为线索二叉树(Threaded binary tree)。

引入线索二叉树的目的是:加快查找结点前驱和后继的速度

注:概念题也是会考的!( ^ ^)/~


2 线索二叉树的构造

2.1 线索二叉树的存储结构

图1 线索二叉树的结点结构

图1 ltag和rtag的设定

简单来说,ltag和rtag的作用:标明指针是否用作线索标志。

二叉树的存储结构代码描述如下:

typedef struct TBTNode{
	ElemType data;	//数据元素
	struct TBTNode *lchild, *rchild;	//左右孩子指针
	int ltag, rtag;		//左右线索标志
}TBTNode;

2.2 给线索二叉树画线索

2.2.1 中序

以下图的一棵二叉树为例,我们首先来讲解一下中序线索二叉树的建立过程:

图1 一棵二叉树

图2 在二叉树上添加中序线索

第一步,写出它的中序遍历序列,于是我们可以得到序列为:CBEGDFA

第二步,根据中序序列,画出线索即可:

从中序遍历序列的第一个结点看起。也就是C结点,因为左右孩子均为空,所以得画上两条线索。因为中序序列CBEGDFA中,C前面没字母,换言之就是在中序序列中没有前驱,所以左边的线索指向NULL,而C的后面一个字母是B,故结点右边的线索指向B结点。

再来看E结点,因为左孩子为空,所以只需画上一条左边的线索。因为中序序列CBEGDFA中,E前面字母是B,换言之就是在中序序列中E的前驱结点是B,所以左边的线索指向B结点。

就这么简单~


2.2.2 先序

以下图的一棵二叉树为例,我们首先来讲解一下先序线索二叉树的建立过程:

图3 一棵二叉树

图4 添加先序线索

第一步,写出它的先序遍历序列,于是我们可以得到序列为:RADEBCFGHK

然后根据遍历序列前后字母位置关系画线索即可。

2.2.3 后序

图5 一棵二叉树

图6 添加后序线索

第一步,写出它的后序遍历序列,于是我们可以得到序列为:bcjdahigfe

然后根据后序遍历序列顺序给空孩子添加前驱后继线索即可。

比如j结点,左右孩子为空,那么需添加两条线索。因为后序遍历序列中j前面是c,后面是d,于是j左线索指向c,右线索指向d

2.3 二叉树线索化代码实现

2.3.1 通过中序遍历线索化

通过中序遍历对二叉树线索化的递归算法如下:

二叉树的存储结构代码:

typedef struct TBTNode{
	char data;	//数据元素
	struct TBTNode *lchild, *rchild;	//左右孩子指针
	int ltag, rtag;		//左右线索标志
}TBTNode;

主程序代码:

void createInThread(TBTNode *root){
    TBTNode *pre = NULL;   //前驱结点指针
    if(root != NULL){	
        InThread(root, pre);
        pre->rchild = NULL;	//处理中序最后的一个结点
        pre->rtag = 1}
}

InThread部分:

void InThread(TBTNode *p, TBTNode *&pre){
    if(p != NULL){
        InThread(p->lchild, pre);  //递归,左子树线索化
        if(p->lchild == NULL){ //建立当前结点的前驱线索
            p->lchild = pre;
            p->ltag = 1;
        } 
        if(pre != NULL && pre->rchild == NULL){ //建立前驱结点的后继线索
            pre->rchild = p;
            pre->rtag = 1;
        }
	pre = p;	//标记当前结点,使其成刚刚访问过的结点
	InThread(p->rchild, pre);  //递归,右子树线索化
    }
}

注:TBTNode * &pre中,“ *& ”表示pre是一个指针类型的引用变量,即它可以引用一个TBTNode*类型的指针,并在函数内部修改所引用的指针的值,而这些修改也会反映到函数外部。由于使用了指针类型的引用变量pre来保存当前遍历节点的前驱节点,因此我们能够利用其记录已经遍历过的节点,在线索化二叉树时连接每个节点的前驱和后继指针。

【注意引用是C++语法,C是不支持的】


2.3.2 通过先序遍历线索化

void preThread(TBTNode *p, TBTNode *&pre){
    if(p != NULL){
        if(p->lchild == NULL){ //建立当前结点的前驱线索
            p->lchild = pre;
            p->ltag = 1;
        } 
        if(pre != NULL && pre->rchild == NULL){ //建立前驱结点的后继线索
            pre->rchild = p;
            pre->rtag = 1;
        }
	pre = p;	//标记当前结点,使其成刚刚访问过的结点
    if(p->ltag == 0)
        preThread(p->lchild, pre);
    if(p->rtag == 0)
        preThread(p->rchild, pre);
    }
}

注:为何这边要加上判断语句if(p->ltag == 0) if(p->rtag == 0)

是为了避免重复遍历同一个结点。如果不判断左右孩子是否为线索,那么程序就有可能把线索也当成左右孩子来访问,从而导致重复遍历同一个结点(绕圈圈~),最终导致栈溢出。

2.3.3 通过后序遍历线索化

void postThread(TBTNode *p, TBTNode *&pre){
    if(p != NULL){
        postThread(p->lchild, pre);  //递归左子树线索化
        postThread(p->lchild, pre);  //递归右子树线索化
        if(p->lchild == NULL){ //建立当前结点的前驱线索
            p->lchild = pre;
            p->ltag = 1;
        } 
        if(pre != NULL && pre->rchild == NULL){ //建立前驱结点的后继线索
            pre->rchild = p;
            pre->rtag = 1;
        }
	pre = p;	//标记当前结点,使其成刚刚访问过的结点
    }
}

3 线索二叉树的遍历

3.1 中序线索二叉树的遍历

第一步,编写一个方法,用来找出中序线索二叉树在中序序列中的第一个结点

TBTNode *First(TBTNode *p){
    while(p->ltag == 0) p = p->lchild;	//相当于找到树的最左下的结点
    return p;
}

第二步,编写一个方法,用来找出一个结点在中序序列中的后续结点

TBTNode *Next(TNTNode *p){
	if(p->rtag == 0){
		return First(p->rchild);
	}
	return p->rchild;		//rtag=1,直接返回后继线索rchild,因为线索化后,rchild就是线索了,指向后继结点
}

整合以上两个方法,即可得到中序线索二叉树的中序遍历算法

void Inorder(TBTNode *root){
    for(TBTNode *p = First(root); p != NULL; p = Next(p))
        Visit(p);	
}

3.2 先序线索二叉树的遍历

void preorder(TBTNode *root){
    if(root != NULL){
        TBTNode *p = root;
        while(p != NULL){
            while(p->ltag == 0){	//左指针不是线索
                Visit(p);
                p = p->lchild;	//那么边访问边向左移
            }
            Visit(p); //此时p左指针一定是线索,于是直接访问
            p = p->rchild	//此时p左孩子不存在,右指针无论是否是线索,都指向前序序列中的后继结点
        }
    }
}

3.3 后序线索二叉树的遍历

在考研数据结构中,只需记住以下三点,对代码要求不高(过于复杂):

  1. 若结点x是二叉树的根,则其后继为空。
  2. 若结点x是其双亲的右孩子,或是其双亲的左孩子且其双亲没有右子树,则其后继即为双亲结点。
  3. 若结点x是其双亲的左孩子,且其双亲有右子树,则其后继为双亲右子树上按后序遍历列出的第一个结点。

代码实现:因为在后序线索二叉树上找后继时,需要知道结点的双亲,所以需要带标志域的三叉链表作为存储结构。这里就不贴出代码了(因为考研不考 只需掌握思想即可)


4 补充知识点

线索二叉树的遍历无需使用栈,因为它利用了隐含在线索二叉树中的前驱和后继信息。线索二叉树相当于一个双向链表

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

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

相关文章

五一欢乐赛!题解

前言 有点一言难尽, 这次本来想的给大火减减压放了很多区域赛和省赛的签到题(还有一些是写过的题), 没想到打的并不是很理想, 还用了分数值想减少一些差距, 结果看起来好像让差距更大了, 尴尬 A题 Frozen Scoreboard https://codeforces.com/gym/104076/problem/D 济南站的中…

【MST+离线】ABC235 E - MST + 1

一开始想的是分类讨论,看那条边加了之后成不成环,如果不成环且权值在前n-1,则一定在MST里,不在前n-1则不在MST里;如果成环了,如果权值不在前n-1,则不在MST里,如果权值在前n-1&#x…

InstructGPT怎么准备和标记数据集

目录 1. 谁标记数据集 2. 数据集的来源 3.数据预处理 4. 标记数据集的方法 5. 数据多样性 6. 这样标记的数据的不足之处 名词解释 1. 谁标记数据集 OpenAI 公司在Upwork平台上和Scale AI公司聘请了大约 40 名承包商为他们标记数据,通过筛选测试(screening tes…

ros基础笔记

1创建工作空间 catkin_init_workspace 将文件夹初始化成ros文件 编译工作空间catkin_make vi ~/.bashrc 加入环境变量bashrc一下在任何终端都生效 catkin_create_pkg learning_communication通讯机制 std_msgs数据结构 rospy roscpp catkin_create_pkg mbot_description ur…

【C++】string类常用接口

目录 一、string类二、string类的常用接口1.string类对象的常见构造2.string类对象的容量操作3.string类对象的访问及遍历操作4.string类对象的修改操作5.string类非成员函数6.vs和g下string结构的说明 一、string类 STL的六大组件: 字符串是表示字符序列的类标准…

E. Train Hard, Win Easy(数学推导 + 前缀和)

Problem - E - Codeforces 这是一个有关竞赛编程的问题。Zibi 是一名竞赛编程教练,有 n 名选手想要备战。培训比赛具有一些不同寻常的规则——每个团队有两名成员和两个问题,每个选手都会编写其中一个问题的代码。当然,一个团队中的人将编写不…

ViveNAS - 一个基于LSM tree的文件存储实现 (一)

1. ViveNAS (GitHub - cocalele/ViveNAS) ViveNAS 是一个开源分布式的网络文件系统(NAS), 具有下面的特点: - 通过不同存储介质的结合,在高性能、低成本间寻找动态的平衡 - 解决数据的长期、低成本存储问题&#xff…

JVM-0428

执行引擎 执行引擎做什么的 执行引擎是Java虚拟机核心的组成部分之一。“虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引…

java基础入门-03

Java基础入门-03 10、字符串10.1.API10.1.1API概述10.1.2如何使用API帮助文档 10.2.String类10.2.1 String类概述10.2.2 String类的特点10.2.3 String类的构造方法10.2.4 创建字符串对象两种方式的区别10.2.5 字符串的比较10.2.5.1 号的作用10.2.5.2 equals方法的作用 10.2.6 用…

java web会话管理

在人机交互过程中,会话管理是指保持用户的整个会话活动的交互与计算机系统跟踪的过程。会话管理分为桌面会话管理、浏览器会话管理、Web会话管理。本书讨论的是Web会话管理(通常指的是session以及Cookie) , 也称为会话跟踪。 会话管理基本原理 使用隐藏…

机器学习笔记 图像特征提取器(卷积变体)的技术发展与演变

一、图像特征提取器简述 图像特征提取器是可用于从图像中学习表示的函数或模块。最常见的特征提取器类型是卷积,其中内核在图像上滑动,允许参数共享和平移不变性。 在深度学习技术的快速发展过程中,基于卷积也演变出来了若干新技术由于图像特征的提取,这里进行了一下简单梳…

[oeasy]python0145_版本控制_git_备份还原

git版本控制 回忆上次内容 上次我们了解了 try 的完全体 try 尝试运行 except 发现异常时运行的代码块 else 没有发现异常时运行的代码块 finally 无论是否发现异常最终都要运行的代码块 发现导入部分 可以再分为两个子模块一个输入 a一个输入 b 可以再拆分么?&…

【Python基础练习100题--第一篇:文件篇】

前言 这些题都是在B站的练习题,链接在这 对于刚学python的新手来说十分的适合, 可以加强和巩固我们的基础。 嘿嘿 一起噶油吧!🍉 🍉1.对学生成绩排序 # 这里对字典进行排序,同事使用到了sorted函数 # 这…

【安全工具】Httpx信息收集

文章目录 前言一、下载二、使用步骤1.帮助文档2.常用命令常用组合命令: 总结 前言 HTTPX 是一个功能强大的 HTTP 客户端工具,用于执行各种网络任务,例如发现 Web 应用程序漏洞、探测域名和端口等。它使用了 retryablehttp 库来运行多个探测器…

[计算机图形学]相机与透镜(前瞻预习/复习回顾)

一、相机 1.成像方法 成像方法有两种,合成和捕捉,我们之前所说的光栅化和光线追踪都属于合成的方法,也就是实际上不存在的东西。而另一种成像方法叫做捕捉成像,也就是把真实世界中存在的一些东西变成照片这就是捕捉成像&#xf…

【量化课程】01_投资与量化投资

文章目录 1.1 什么是投资1.1.1 经济意义上的投资1.1.2 投资的分类1.1.3 金融投资1.1.4 个人投资者投资品种1.1.5 投资VS投机 1.2 股票投资的基本流程1.3 常见的股票投资分析流派1.3.1 投资者分析流派 1.4 什么是量化投资1.4.1 量化投资基本概念1.4.2 量化投资的优势1.4.3 量化投…

C多线程、锁、同步、信号量

文章目录 一 线程函数1.1 创建线程1.2 线程退出1.3 线程回收1.4 线程分离:1.5 其他线程函数1.5.1 线程取消1.5.2 线程ID比较 二 线程同步2.1 互斥锁2.1.1定义2.1.2 初始化2.1.3 销毁2.1.4 加锁 、 常试锁、解锁2.1.5 互斥锁使用 2.2 死锁2.3 如何避免死锁2.4 读写锁…

05_从0运行,重定位,初始化,中断再到定时器

总结 这边简单讲讲,代码上电后从0开始发生了什么,为什么要重定位把代码复制到sdram, bin文件前面几条跳转函数都跳转去哪 中断产生后发生什么 重定位问题 1.为什么需要重定位 把程序从一个位置移到另一个位置 叫重定位 可以只重定位部分段的数据 也可以把所有的都重定位到sd…

Linux运维之shell基础

一.流程控制 1.if判断 基本语法: if [ 条件判断式 ] then 程序 elif [ 条件判断式 ] then 程序 else 程序 fi注意事项: ①[ 条件判断式 ],中括号和条件判断式之间必须有空格②if 后要有空格 例如,现在写一个if.sh脚本文件 #…

echarts关于自定义饼图数据刷新和颜色渲染问题

在使用echarts的自定义饼图Customized Pie时,定义的动态数据会发生颜色无法渲染的问题,如下图所示: 该图表的颜色是根据itemStyle内的color属性而来,如下: itemStyle: {color: #4d90fe, /* 图表的颜色 */shadowBlur:…