线索二叉树的前序遍历

news2024/12/25 9:17:20

线索二叉树原理
遍历二叉树的其实就是以一定规则将二叉树中的结点排列成一个线性序列,得到二叉树中结点的先序序列、中序序列或后序序列。这些线性序列中的每一个元素都有且仅有一个前驱结点和后继结点。

但是当我们希望得到二叉树中某一个结点的前驱或者后继结点时,普通的二叉树是无法直接得到的,只能通过遍历一次二叉树得到。每当涉及到求解前驱或者后继就需要将二叉树遍历一次,非常不方便。

于是是否能够改变原有的结构,将结点的前驱和后继的信息存储进来。
————————————————
原文链接:https://blog.csdn.net/S_999999/article/details/86157532

总结:即利用起每个节点的空链域,使其指向其前驱或后继,排序后在头或为且有空领域没有前驱或后继则指向空。我们创建树的时候,定义lTag,lTag,都先赋值为0,当其左右子树为空时将l/rTag赋值为1.

树的图形状:

代码思路及其参代码(含讲解)

我们根据树的形状先创建其代码构建树

结构体的创建
typedef DataType;
typedef struct TreeNode
{
	DataType data;//存放的数据
	struct TreeNode* left, * right;//左右孩子指针
	int lTag, rTag;//左右是否为空领域的标志
}ThreadTreeNode;

 

构建树:
int main()
{
	ThreadTreeNode* A = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	A->left = NULL;
	A->right = NULL;
	A->data = 'A';
	A->lTag = 0;
	A->rTag = 0;
	ThreadTreeNode* B = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	B->left = NULL;
	B->right = NULL;
	B->data = 'B';
	B->lTag = 0;
	B->rTag = 0;
	ThreadTreeNode* C = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	C->left = NULL;
	C->right = NULL;
	C->data = 'C';
	C->lTag = 0;
	C->rTag = 0;
	ThreadTreeNode* D = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	D->left = NULL;
	D->right = NULL;
	D->data = 'D';
	D->lTag = 0;
	D->rTag = 0;
	ThreadTreeNode* E = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	E->left = NULL;
	E->right = NULL;
	E->data = 'E';
	E->lTag = 0;
	E->rTag = 0;
	//赋值
	A->data = 'A';
	A->left = B;
	A->right = C;
	B->data = 'B';
	B->left = D;
	B->right = E;
    return 0;
}

 前序线索二叉树的构造

思路:

二叉树的线索化过程,分为两大步:

第一步:使用前序遍历,将二叉树线索化

1.定义一个全局变量当作前驱节点(每次节点遍历的时候其前驱节点一直在改变所以用全局变量)

preNode=NULL;

2. 使用前序遍历,递归的为每个节点线索化

a.给当前节点设置前驱节点

检查当前节点的左孩子节点是否为空

若为空,则对齐左指针线索化,指向preNode(上一次访问的节点)

若不为空,则递归的向左访问

b.给当前节点设置后继节点(给前一个节点设置其后继线索eg如我们上图的D判断其前一个节点B的右子树是否为空,若为空则右子树指向当前指针D,若不为空则不做处理)

c.设置当前节点为前一个节点(preNode的迭代)

3.前序访问的时候最后一个结点右指针没后继,则要单独处理让右指针的rTag赋值为1.

第二部:再定义一个函数,重新遍历线索二叉树

ThreadTreeNode* preNode = NULL;
void PreOrderThread(ThreadTreeNode* node)
{
	if (node == NULL)
	{//判空,若节点为空则直接返回
		return;
	}
	if (node->left == NULL)
	{//如果当前节点不为空且其左子树为空则对齐左子树进行线索化
		node->left = preNode;
		node->lTag = 1;
	}
//因为我们不知道后继节点,所以我们就遍历到下一个节点的时候再去设置上一个节点的的后继,此时上一个节点就是当前节点的前驱节点preNode,对其preNode的右子树进行判断
	if (preNode != NULL && preNode->right == NULL)
	{//如果前驱节点不为空且前驱节点的右孩子为空则对其设置后继(右子树线索化)
		preNode->right = node;
		preNode->rTag = 1;
	}
	preNode = node;//线索化完后我们让当前节点赋值为前驱节点使其进行迭代
	if (node->lTag == 0)//我们在访问其左子树的时候要先对其tag判断,如果其tag为0则说明其左子树不为空链域或还没有被线索化。如果我们不判断则当期访问ltag为1的节点则会访问到当前节点左子树线索化后指向的节点就是其前驱节点,这样会进入死循环,右子树同理也要进行判断
	{
		PreOrderThread(node->left);
	}
	if (node->rTag == 0)
	{
		PreOrderThread(node->right);
	}
}
//因为前序线索化时最后一个指针的右子树没有后继,则要对其单独处理,但也不能再函数递归过程中进行防止每次函数递归都会处理一次所以我们单独当整个递归完后再进行设置。
//解决方法:我们把线索化递归放进一个函数中,使其先进性线索化的递归,等整个线索化函数结束后再去处理最后一个指针,因为preNode一直在按前序顺序迭代,当迭代到最后一次的时候正好为最后一个节点,要让最后一个节点的rTag赋值为1,则直接让preNode的rTag为1即可。
void CreatePreOrderThread(ThreadTreeNode* root)
{
	preNode = NULL;
	if (root != NULL)
	{
		PreOrderThread(root);
		if (preNode != NULL)
		{
			preNode->rTag = 1;
		}
	}
}

 遍历线索化后的二叉树

 

利用递归思路进行遍历访问
void PreOrder(ThreadTreeNode* node)
{//先判空,不为空我们再进行处理
	if (node != NULL)
	{
		printf("%c->", node->data);
//我们再进行递归前先判断其lTag是否为0,如果为0则说明其左子树在线索化前不为空链域,则可以访问其左子树,如果不为0,则说明其左为空领域不能递归访问。我们不需要对右子树进行判断,因为右子树是否为空它都会按前序的顺序一直向后走不会出现循环
		if (node->lTag == 0)
		{
			PreOrder(node->left);
		}//lTag和rTag为串行关系,要么lTag要么rTag,我们不需要对右子树进行判断
		else
		{
			PreOrder(node->right);
		}
	}
}

总代码: 

#include <stdio.h>
#include  <stdlib.h>

typedef DataType;
typedef struct TreeNode
{
	DataType data;
	struct TreeNode* left, * right;
	int lTag, rTag;
}ThreadTreeNode;

ThreadTreeNode* preNode = NULL;
void PreOrderThread(ThreadTreeNode* node)
{
	if (node == NULL)
	{
		return;
	}
	if (node->left == NULL)
	{
		node->left = preNode;
		node->lTag = 1;
	}
	if (preNode != NULL && preNode->right == NULL)
	{
		preNode->right = node;
		preNode->rTag = 1;
	}
	preNode = node;
	if (node->lTag == 0)
	{
		PreOrderThread(node->left);
	}
	if (node->rTag == 0)
	{
		PreOrderThread(node->right);
	}
}
void CreatePreOrderThread(ThreadTreeNode* root)
{
	preNode = NULL;
	if (root != NULL)
	{
		PreOrderThread(root);
		if (preNode != NULL)
		{
			preNode->rTag = 1;
		}
	}
}
void PreOrder(ThreadTreeNode* node)
{
	if (node != NULL)
	{
		printf("%c->", node->data);
		if (node->lTag == 0)
		{
			PreOrder(node->left);
		}
		else
		{
			PreOrder(node->right);
		}
	}
}
int main()
{
	ThreadTreeNode* A = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	A->left = NULL;
	A->right = NULL;
	A->data = 'A';
	A->lTag = 0;
	A->rTag = 0;
	ThreadTreeNode* B = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	B->left = NULL;
	B->right = NULL;
	B->data = 'B';
	B->lTag = 0;
	B->rTag = 0;
	ThreadTreeNode* C = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	C->left = NULL;
	C->right = NULL;
	C->data = 'C';
	C->lTag = 0;
	C->rTag = 0;
	ThreadTreeNode* D = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	D->left = NULL;
	D->right = NULL;
	D->data = 'D';
	D->lTag = 0;
	D->rTag = 0;
	ThreadTreeNode* E = (ThreadTreeNode*)malloc(sizeof(ThreadTreeNode));
	E->left = NULL;
	E->right = NULL;
	E->data = 'E';
	E->lTag = 0;
	E->rTag = 0;
	//赋值
	A->data = 'A';
	A->left = B;
	A->right = C;
	B->data = 'B';
	B->left = D;
	B->right = E;
	CreatePreOrderThread(A);
	PreOrder(A);
		return 0;
}

运行结果:

 

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

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

相关文章

计算机组成原理---第 6 章总线系统

一、总线的概念和结构形态 总线的基本概念 ⑴概述 总线是构成计算机系统的互联机构&#xff0c;是多个系统功能部件之间进行数据传送的公共通路。 ⑵ 分类 总线的分类方式有很多&#xff1a;如被分为外部总线和内部总线、系统总线和非系统总线、片内总线和PCB板级总线、串行…

VS2022+opengl环境配置

glfw下载Download | GLFW glad下载https://glad.dav1d.de/ Freeglut下载 https://freeglut.sourceforge.net/index.php#download cmake下载 Download | CMake glfwFreeglut 用cmake配置Freeglut&#xff0c;生成vs工程项目&#xff0c;用vs2022编译项目&#xff0c;生成fr…

27.Spring的事务控制

目录 一、编程式事务控制相关对象。 &#xff08;1&#xff09;事务管理器。 &#xff08;2&#xff09;事务定义信息对象&#xff08;如隔离级别、传播行为&#xff09;。 &#xff08;3&#xff09;事务状态对象。 &#xff08;4&#xff09; 知识要点。 二、声明式事务…

基于STM32的开源简易示波器项目

目录 ​一、前言 二、硬件接线 三、信号的采集 四、代码配置 五、数据的处理 六、模拟正弦波输出 七、模拟噪声或三角波输出 八、显示函数与按键控制 ​一、前言 该项目是基于正点原子精英板制作的一个简易示波器&#xff0c;可以读取信号的频率和幅值&#xff0c;并可…

JetpackCompose从入门到实战学习笔记14

JetpackCompose从入门到实战学习笔记14——Coli的简单使用 1.简介&#xff1a; Coil 是一个 Android官方出的配合Jetpack的图片加载库&#xff0c;通过 Kotlin 协程的方式加载图片。 优点如下&#xff1a; 更快: Coil 在性能上有很多优化&#xff0c;包括内存缓存和磁盘缓存…

C/C++基础知识

专栏&#xff1a;C/C 个人主页&#xff1a; C/C基础知识 前言C关键字(C98)命名空间命名空间的定义正常的命名空间的定义如何使用命名空间 命名空间可以嵌套同一个工程中允许存在多个相同名称的命名空间&#xff0c;编译器最后会合成同一个命名空间中(一个工程中的.h文件和test.…

(数字图像处理MATLAB+Python)第七章图像锐化-第一、二节:图像锐化概述和微分算子

文章目录 一&#xff1a;图像边缘分析二&#xff1a;一阶微分算子&#xff08;1&#xff09;梯度算子A&#xff1a;定义B&#xff1a;边缘检测C&#xff1a;示例D&#xff1a;程序 &#xff08;2&#xff09;Robert算子A&#xff1a;定义B&#xff1a;示例C&#xff1a;程序 &a…

Tailscale: Please Restart the Tailscale Windows Service

之前用的好好的&#xff0c;最近重新升级了一下Tailscale后发现一直连不上。右击win10右下角的Tailscale图标&#xff0c;第一行显示&#xff1a;Please Restart the Tailscale Windows Service。 我查看了一下服务&#xff0c;发现Tailscale是自动的&#xff0c;这里的启动类…

vuex存储数组(新建,增,删,更新),并存入localstorage定时删除

vuex存储数组(新建&#xff0c;增&#xff0c;删&#xff0c;更新)&#xff0c;并存入localstorage定时删除 本文目录 vuex存储数组(新建&#xff0c;增&#xff0c;删&#xff0c;更新)&#xff0c;并存入localstorage定时删除使用背景store中实现增删改组件中维护数组&#x…

缩小数据文件

今天又出现12.2c 环境的问题&#xff0c;1T的数据空间还剩下2G&#xff0c;吓了一身冷汗&#xff0c;赶紧查看原因&#xff0c;不知道哪路业务大神作妖了。 发现sysaux和system增加N多数据文件&#xff0c;而且目前使用不多&#xff0c; 缩小表空间的数据文件 可以使用下面的语…

直升机空气动力学基础---002 桨叶的主要参数

源于 1.桨叶的平面形状和主要参数 由于其设计制造比较简单&#xff0c;早期直升机大多采用矩形桨叶&#xff0c;缺点是在高速气流中&#xff0c;无法抑制桨尖涡&#xff0c;会消耗向下的诱导速度&#xff0c;降低旋翼的拉力。现代多采用梯形桨叶。 桨尖后掠能够降低桨尖涡 …

【Linux】Linux基本指令(2)

一.你如何看待指令 指令说白了就是可执行程序&#xff0c;且指令一定是在系统的某一个位置存在的&#xff0c;在执行指令前&#xff0c;我们需要先找到它。 二.man指令 众所周知&#xff0c;Linux的指令有很多&#xff0c;指令的选项也有很多&#xff0c;我们不可能全记住&…

android注解注入AspectJ面向切面AOP插桩技术改变android原生类对象行为记录View点击事件,Java(3)

droid注解注入AspectJ面向切面AOP插桩技术改变android原生类对象行为记录View点击事件&#xff0c;Java&#xff08;3&#xff09; 动态改变Toast提示的内容&#xff0c;弹之前修改。Button在每次点击后记录。 import android.util.Log; import android.widget.Toast;import o…

基于天牛须(BAS)与NSGA-Ⅱ混合算法的交直流混合微电网多场景多目标优化调度(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

论Shell编程规范与变量

目录 一&#xff1a;shell脚本 1.shell概述 2.linux中包含的常用shell 3.shell脚本概述 4.shell脚本应用场景 5.shell脚本的作用 6.用户的登录shell 二&#xff1a; 编写脚本 1.脚本的基本格式 2.shell脚本的执行 3. 交互式硬件设备 4.重定向操作 5.管道操作符号 “…

信号完整性分析基础知识之传输线和反射(一):阻抗变化引起反射

阻抗不连续引起的反射和失真可能会导致信号的误触发和误码&#xff0c;这是导致信号失真和质量下降的主要原因。 在某些情况下&#xff0c;这看起来像振铃。当信号电平下降时&#xff0c;下冲会影响噪声预算并导致误触发。或者&#xff0c;在下降信号上&#xff0c;峰值可能会上…

阅读笔记 First Order Motion Model for Image Animation

文章解决的是图片动画的问题。假设有源图片和驱动视频&#xff0c;并且其中的物体是同一类的&#xff0c;文章的方法让源图片中的物体按照驱动视频中物体的动作而动。 文章的方法只需要一个同类物体的视频集&#xff0c;不需要而外的标注。 方法 该方法基于self-supervised策…

Qt信号槽原理

Qt之信号槽原理 一.概述 所谓信号槽&#xff0c;实际就是观察者模式。当某个事件发生之后&#xff0c;比如&#xff0c;按钮检测到自己被点击了一下&#xff0c;它就会发出一个信号&#xff08;signal&#xff09;。这种发出是没有目的的&#xff0c;类似广播。如果有对象对这…

idea无效的目标版本和类文件具有错误的版本 61.0, 应为 52.0错误(测试有用,一次性解决问题)

SpringBoot己更新到3后&#xff0c;使用的JAVA版本最低要求JAVA17&#xff0c;如果低于这个版本就是报错&#xff1a; 问题一&#xff1a;类文件具有错误的版本 61.0, 应为 52.0。 解决就只有升级JAVA-sdk&#xff1a; 官方下载地址&#xff1a;JAVA20-17 官方推荐更好的sd…

MySQL 视图、函数和存储过程

MySQL 是一种流行的关系型数据库管理系统&#xff0c;其具有强大的功能和灵活性&#xff0c;使其成为了许多企业和个人喜爱的数据库选择。在 MySQL 中&#xff0c;视图、函数和存储过程是常见的数据库对象&#xff0c;它们都有助于提高数据的处理效率和可重用性。 一、视图 视…