有趣的数据结构算法16——线索二叉树的构建

2023-11-14

深度遍历不仅仅有递归的方法噢,还有通过建立线索二叉树进行遍历的方法!
在这里插入图片描述

什么是线索二叉树

在二叉树的结点上加上线索的二叉树称为线索二叉树,本文所讲述的线索二叉树主要用于中序遍历。
定义二叉树的时候,我们定义的每一个结点都是属于同一个结构体的,对于二叉树中的叶子结点来说,它的两个子结点都是指向NULL的,这样就造成了结构体的资源浪费。如果能将这些为空的子结点利用起来,将会大大增强二叉树的资源利用率!
常规的二叉树如图所示:
在这里插入图片描述
可以看到二叉树的许多结点的子结点指向的都是NULL,这些资源是没有被利用的。如果能将其利用起来,将大大增强二叉树的资源利用率。
线索二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继。
线索二叉树是利用二叉树的空链指针完成前驱和后继的添加,利用前驱后继完成中序遍历。

线索二叉树的实现形式

本文使用树的生成方式可以参照我的blog,其具体结点状况如下:
https://blog.csdn.net/weixin_44791964/article/details/98229328
在这里插入图片描述
线索二叉树相比于普通二叉树增加了一个结点p,该结点实际上并不属于真正的树,该结点的存在有助于线索二叉树的生成与遍历。
在最初状态下,p结点左子树指向根节点,右子树指向自身。
在这里插入图片描述
根据每个结点的定义可知,每个结点接有两个子树,分别为左子树和右子树,左子树所连接的结点是要进行遍历的上一个结点,右子树所连接的结点是要进行遍历的下一个结点。
但是对于左下角的结点c而言,其为中序遍历的第一个结点,无上一个结点,因此将其左结点定义为结点p。
但是对于右下角的结点d而言,其为中序遍历的最后一个结点,无下一个结点,因此将其右结点定义为结点p。
此时原子树可以形成如下图所示的闭环。
在这里插入图片描述

线索二叉树的代码实现

线索二叉树的初始化

与普通二叉树不同的是,线索二叉树的结构体增加了两个元素,分别是ltag和rtag,分别用于指示左子树与右子树此时指向的是子结点还是线索
其具体定义如下:

//Link代表0,表示此时指向左右孩子
//Thread代表1,表示此时指向前驱后继的指针
typedef enum {Link,Thread} PointerTag;

struct Node{
	Ele data;		//data用于存储信息
	struct Node* LeftChild;
	struct Node* RightChild;
	PointerTag ltag;
	PointerTag rtag;
};

typedef struct Node Node, *Tree;

在完成结构体的定义后,要进行线索二叉树的初始化,在初始化的时候,我们先假设所有结点的子树都指向左右孩子。

void init(Tree* t){	//当输入的字符为空格的时候,代表叶子结点
	//输入其它的字符代表当前结点的data值
	//输入顺序为当前结点、左结点、右结点。
	Ele c;
	scanf("%c", &c);
	if (' ' == c){
		*t = NULL;
	}
	else{
		*t = (Tree)malloc(sizeof(Node));
		if (*t == NULL){
			printf("内存分配失败");
			exit(1);
		}
		(*t)->data = c;
		(*t)->ltag = Link;
		(*t)->rtag = Link;
		init(&(*t)->LeftChild);		//生成左结点
		init(&(*t)->RightChild);	//生成右结点
	}
}

此时输入合适的字符即可生成二叉树。

线索的串联

在进行所有的结点串联前,应当定义全局变量。

typedef struct Node Node, *Tree;
Tree pre;

该结点用于在进行结点串联时保存上一个结点的地址。
与此同时,由于线索二叉树相对于普通二叉树添加了一个p结点,需要在main函数中进行定义。

int main(){
	Tree p,BiTree = NULL;
	init(&BiTree);
	InitThreading(&p,BiTree);
	printf("中序遍历的结果为:");
	scan(p);
	printf("\n");
	return 0;
}

在main函数中,p元素被传入InitThreading函数进行初始化并完成线索的串联过程。
InitThreading函数的代码如下:

void InitThreading(Tree *p, Tree T){
	*p = (Tree)malloc(sizeof(Node));
	(*p)->ltag = Link;
	(*p)->rtag = Thread;		
	(*p)->RightChild = *p;		//p结点右子树指向自身
	if (!T){
		(*p)->LeftChild = *p;	//当树为空时,p结点的左右子树都指向自身
	}
	else{
		(*p)->LeftChild = T;	//p结点左子树指向根节点
		pre = *p;				//在最初情况下pre指向p
		Threading(T);
		pre->RightChild = *p;
		pre->rtag = Thread;		//使得中序遍历最后一个结点的右子树指向结点p
		(*p)->RightChild = pre;		//使得结点p的右子树指向中序遍历最后一个结点。

	}  
}

在InitThreading函数中,Threading函数用于线索的串联。
具体代码如下:

void Threading(Tree t){
	if (t != NULL){
		Threading(t->LeftChild);
		if (!t->LeftChild){			//如果没有左孩子,则将左孩子指向上一个访问的结点
			t->ltag = Thread;
			t->LeftChild = pre;
		}
		if (!pre->RightChild){			//如果没有左孩子,则将左孩子指向上一个访问的结点
			pre->rtag = Thread;
			pre->RightChild = t;
		}
		pre = t;

		Threading(t->RightChild);
	}
}

全部实现代码

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

typedef char Ele;

//Link代表0,表示此时指向左右孩子
//Thread代表1,表示此时指向前驱后继的指针
typedef enum {Link,Thread} PointerTag;

struct Node{
	Ele data;		//data用于存储信息
	struct Node* LeftChild;
	struct Node* RightChild;
	PointerTag ltag;
	PointerTag rtag;
};

typedef struct Node Node, *Tree;

Tree pre;

void init(Tree* t){	//当输入的字符为空格的时候,代表叶子结点
	//输入其它的字符代表当前结点的data值
	//输入顺序为当前结点、左结点、右结点。
	Ele c;
	scanf("%c", &c);
	if (' ' == c){
		*t = NULL;
	}
	else{
		*t = (Tree)malloc(sizeof(Node));
		if (*t == NULL){
			printf("内存分配失败");
			exit(1);
		}
		(*t)->data = c;
		(*t)->ltag = Link;
		(*t)->rtag = Link;
		init(&(*t)->LeftChild);		//生成左结点
		init(&(*t)->RightChild);	//生成右结点
	}
}

void Threading(Tree t){
	if (t != NULL){
		Threading(t->LeftChild);
		if (!t->LeftChild){			//如果没有左孩子,则将左孩子指向上一个访问的结点
			t->ltag = Thread;
			t->LeftChild = pre;
		}
		if (!pre->RightChild){			//如果没有左孩子,则将左孩子指向上一个访问的结点
			pre->rtag = Thread;
			pre->RightChild = t;
		}
		pre = t;

		Threading(t->RightChild);
	}
}

void InitThreading(Tree *p, Tree T){
	*p = (Tree)malloc(sizeof(Node));
	(*p)->ltag = Link;
	(*p)->rtag = Thread;		
	(*p)->RightChild = *p;		//p结点右子树指向自身
	if (!T){
		(*p)->LeftChild = *p;	//当树为空时,p结点的左右子树都指向自身
	}
	else{
		(*p)->LeftChild = T;	//p结点左子树指向根节点
		pre = *p;				//在最初情况下pre指向p
		Threading(T);
		pre->RightChild = *p;
		pre->rtag = Thread;		//使得中序遍历最后一个结点的右子树指向结点p
		(*p)->RightChild = pre;		//使得结点p的右子树指向中序遍历最后一个结点。

	}  
}
void visit(Ele e){
	printf("%c", e);
}

void scan(Tree p){
	Tree temp;
	temp = p->LeftChild;		//取得根节点
	while (temp != p){
		while (temp->ltag == Link){		//取得线索二叉树遍历的第一个结点
			temp = temp->LeftChild;
		}
		visit(temp->data);
		while (temp->rtag == Thread && temp->RightChild != p){
			temp = temp->RightChild;

			visit(temp->data);
		}
		temp = temp->RightChild;
	}
}
int main(){
	Tree p,BiTree = NULL;
	init(&BiTree);
	InitThreading(&p,BiTree);
	printf("中序遍历的结果为:");
	scan(p);
	printf("\n");
	return 0;
}

实现结果为:
在这里插入图片描述

GITHUB下载连接

https://github.com/bubbliiiing/Data-Structure-and-Algorithm

希望得到朋友们的喜欢。
有问题的朋友可以提问噢。

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

有趣的数据结构算法16——线索二叉树的构建 的相关文章

  • 第二十八节、基于深度学习的目标检测算法的综述(附代码,并附有一些算法英文翻译文章链接))...

    在前面几节中 我们已经介绍了什么是目标检测 以及如何进行目标检测 还提及了滑动窗口 bounding box 以及IOU 非极大值抑制等概念 这里将会综述一下当前目标检测的研究成果 并对几个经典的目标检测算法进行概述 本文内容来自基于深度学
  • 数据结构----链式栈

    目录 前言 链式栈 操作方式 1 存储结构 2 初始化 3 创建节点 4 判断是否满栈 5 判断是否空栈 6 入栈 7 出栈 8 获取栈顶元素 9 遍历栈 10 清空栈 完整代码 前言 前面我们学习过了数组栈的相关方法 链接 线性表 栈 栈
  • 用 Java 实现的八种常用排序算法

    八种排序算法可以按照如图分类 前置知识 1 算法稳定性 在一个序列中 能保证两个相等的数 经过排序之后 其在序列的前后位置顺序不变 A1 A2 排序前 A1 在 A2 前面 排序后 A1 还在 A2 前面 2 时间复杂度 时间复杂度是用于衡
  • 常用的十种算法--马踏棋盘算法

    1 马踏棋盘算法介绍 马踏棋盘算法也被称为骑士周游问题 将马随机放在国际象棋的 8 8 棋盘 Board 0 7 0 7 的某个方格中 马按走棋规则 马走日字 进行移动 要求每个方格只进入一次 走遍棋盘上全部 64 个方格 2 马踏棋盘算法
  • DNG格式解析

    Author Maddock Date 2015 04 22 转载请注明出处 http www cnblogs com adong7639 p 4446828 html DNG格式基本概念 DNG格式是在TIFF的基础上扩展出来的 要了解D
  • 递归算法中的时间复杂度分析

    对于一种算法的时间复杂度分析还是特别重要的 在一些非递归算法中 我们仅仅看运算次数最多的那一行代码可能执行多少次就可以 实际就是看在循环中变量的变化 但是对于递归算法中该怎么分析呢 下面介绍几种递归函数中的算法时间复杂度分析的方法 0 递推
  • 前缀、中缀、后缀表达式和二叉树

    概念 前缀表达式 Prefix Notation 是指将运算符写在前面操作数写在后面的不包含括号的表达式 而且为了纪念其发明者波兰数学家Jan Lukasiewicz 所以前缀表达式也叫做 波兰表达式 后缀表达式 Postfix Notat
  • CH7-查找

    文章目录 1 查找的基本概念 2 线性表的查找 2 1 顺序查找 线性查找 算法2 1 0 类型定义 算法2 1 1 顺序查找 算法2 1 2 改进后的顺序查找 性能分析 2 2 折半查找 二分或对分查找 算法2 2 1 非递归算法 算法2
  • [共同学习] 平衡二叉树浅见

    平衡二叉树 平衡二叉树的概念 AVL树结点的定义 AVL树的插入 左左 右单旋 右右 左单旋 左右 先左旋 再右旋 右左 先右旋 再左旋 AVL树的验证 验证其为二叉搜索树 验证其为平衡树 AVL树的性能 AVL树的实现 感悟 以上 二叉搜
  • 以太坊系列之十五: 以太坊数据库

    以太坊数据库中都存了什么 以太坊使用的数据库是一个NOSQL数据库 是谷歌提供的开源数据leveldb 这里尝试通过分析以太坊数据库存储了什么来分析以太坊可能为我们提供哪些关于区块链的API 存储内容 NOSQL是一个key value数据
  • 字符串09--表示数值的字符串

    字符串09 表示数值的字符串 jz53 题目概述 解析 参考答案 注意事项 说明 题目概述 算法说明 请实现一个函数用来判断字符串是否表示数值 包括整数和小数 例如 字符串 100 5e2 123 3 1416 和 1E 16 都表示数值
  • JavaScript系列——数组元素左右移动N位算法实现

    引言 在自己刚刚毕业不久的时候 去了一家公司面试 面试官现场考了我这道题 我记忆深刻 当时没有想到思路 毫无疑问被面试官当成菜鸟了 最近刚好在研究数组的各种算法实现 就想到这道题 可以拿来实现一下 纪念自己逝去的青春 需求 假设有这样一个数
  • 二叉树的各种操作函数

    include source h 满与空的问题 计算个数时 判断rear和front的大小1 2 空一个 void InitQueue Queue u u front 0 u rear 0 int sizeQue Queue u int s
  • OJ-合并两个有序链表

    题目描述 代码如下 Definition for singly linked list struct ListNode int val struct ListNode next struct ListNode mergeTwoLists s
  • 数据结构与算法-列表(双向链表)设计及其排序算法

    0 概述 本文主要涵盖列表 双向链表 的设计及其排序算法的总结 列表是一种典型的动态存储结构 其中的数据 分散为一系列称作节点 node 的单位 节点之间通过指针相互索引和访问 为了引入新节点或删除原有节点 只需在局部调整少量相关节点之间的
  • 区块链中的哈希算法

    区块链中的密码学 密码学在区块链中的应用主要有两个 哈希算法与非对称加密算法 这次主要对哈希算法进行详细的说明 哈希算法 哈希算法的特点有 1 输入可以为任意大小的字符串 2 产生固定大小的输出 3 可以在合理的时间内算出输出值 若要满足密
  • 【试题】排列组合

    在写一个远程的代码 如果本地有M个显示器 远程有N个显示器 M lt N 依据分辨率 显示器刷新频率等要求 需要对远程的N个显示器进行最佳分辨率修改 之后 需要从N个远程显示器中选择M个 跟本地显示器进行一对一的匹配 即从 A N M N
  • 雪糕的最大数量 排序+贪心

    雪糕的最大数量 雪糕的最大数量 题目描述 样例 数据范围 思路 代码 题目描述 夏日炎炎 小男孩 Tony 想买一些雪糕消消暑 商店中新到 n 支雪糕 用长度为 n 的数组 costs 表示雪糕的定价 其中 costs i 表示第 i 支雪
  • 二叉树(接口函数的实现)

    今天继续来分享的是二叉树 我们废话不多说 直接来看下面的几个接口函数 然后我们把他们实现 我们就掌握二叉树的二分之一 今天粉丝破千了 属实有点高兴了 typedef char BTDataType typedef struct Binary
  • 【数据结构】单链表的定义和操作

    目录 1 单链表的定义 2 单链表的创建和初始化 3 单链表的插入节点操作 4 单链表的删除节点操作 5 单链表的查找节点操作 6 单链表的更新节点操作 7 完整代码 嗨 我是 Filotimo 很高兴与大家相识 希望我的博客能对你有所帮助

随机推荐

  • MySQL之DML操作

    MySQL之DML操作 1 什么是DML操作 2 插入记录 insert 3 更新记录 update 4 删除记录 delete 1 什么是DML操作 DML是指数据操作语言 英文全称是Data Manipulation Language
  • 最难以理解的排序算法 - 堆排序(超详解)

    堆排序基本介绍 堆排序是利用堆这种数据结构而设计的一种排序算法 堆排序是一种选择排序 它的最坏 最好 平均时间复杂度均为O nlogn 它也是不稳定排序 要理解堆排序 必须先要理解堆这种数据结构 堆是具有以下性质的完全二叉树 每个结点的值都
  • Java 数据结构之双向链表

    版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net lovoo article details 51771321 一 概述 1 什么时双向链表 链表中的每个节点即指向前面一个节点 也指向后面一个节点
  • 解决Mysql (1064) 错误: 1064 - You have an error in your SQL syntax;

    我在给数据库中的表添加数据的时候 写的语句是 INSERT INTO order VALUES 2 编号B 表结构 出现了错误 INSERT INTO order VALUES 2 编号B 1064 You have an error in
  • Spring AOP (二)

    下面介绍 AspectJ语法基础 一 切点表达式函数 AspectJ的切点表达式由关键字和操作参数组成 如execution greetTo 的切点表达式 execution为关键字 而 greetTo 为操作参数 两者联合起来表示目标类g
  • cv2.VideoCapture()

    一 语法 cap cv2 VideoCapture 0 说明 参数0表示默认为笔记本的内置第一个摄像头 如果需要读取已有的视频则参数改为视频所在路径路径 例如 cap cv2 VideoCapture video mp4 二 语法 cap
  • el-tab 切换时添加动画

    需求 在点击切换页面的时候添加动画 解决 用的是 Animate css 1 安装依赖 npm install animate css save 2 在main js里面引入 import animate css 3 在页面中使用 第一步
  • 断开的管道 java.io.IOException: Broken pipe

    此类报错首次接触 在阅览一些文章后 总结如下 pipe是管道的意思 管道里面是数据流 通常是从文件或网络套接字读取的数据 当该管道从另一端突然关闭时 会发生数据突然中断 即是broken 对于文件File来说 这可能是文件安装在已断开连接的
  • VM ware14在win10系统出现虚拟机繁忙/无法正常启动、关闭虚拟机

    VM版本 VM warestation14 windows版本 Windows10 Linux版本 CentOS 7 出现的一些问题 1 无法正常关闭虚拟机 关机界面最后的单词显示为 halting 并一直呈该状态 2 强制关闭虚拟机电源后
  • 【C语言进阶】重新认识字符型变量

    引例 首先我们看一个简单的例子 include
  • 再谈递归——直接法 vs 递归法

    直接法就是有一个直接的思路 算法来解决问题 什么样的数据结构 第一步干什么 然后干什么 最后干什么 递归法的感觉是 好像也没想出什么具体的算法 莫名其妙的就把题解了 解完也没什么深刻的印象怎么解的 因为递归就是base case 递推 而b
  • SQLSever创建表和约束

    表的基本概念 概念 由数据按一定的顺序和格式构成的数据集合 是数据库的主要对象 每一行代表一个记录 每一列代表一个属性 设计表 创建前考虑如下特征 表中要包含数据类型 表中列数 每一列中的数据类型 那些列允许空值 是否使用以及何时约束 那些
  • eclipse实现前后端交互的初步操作

    首先new创建 选择Other 在最下面 然后 然后next起名 再两次next后进行选择 创建完成如下 所有的前端代码写在WebContent里面 所有的Java代码写在Java Resource里的src里面 创建html文件 在win
  • CSS之背景样式及边框样式

    1 背景样式 常用属性 background color 背景颜色 background image 背景图片 background repeat 背景图片的平铺方式 background position 背景图片的位置 backgrou
  • 加密、解密、加签、验签专题

    首先明确几个名词 加密 发送方利用接收方的公钥对要发送的明文进行加密 解密 接受方利用自己的私钥进行解密 公钥和私钥配对的 用公钥加密的文件 只有对应的私钥才能解密 当然也可以反过来 用私钥加密 用对应的公钥进行解密 签名 发送方用一个哈希
  • 智能家居系统中网关与服务器如何连接?

    原文点击打开链接 在新型智能家居系统中 家庭网关将取代PC机作为家庭控制中心 传统客户端 服务器模式不能保持家庭网关与远程服务器实时连接 基于百万级的家庭网关与服务器保持长连接的目的 采用主从服务器框架进行负载均衡 心跳机制保障网关与服务器
  • 容器安全最佳实践入门

    作者 Cloudberry 译者 王者 策划 万佳 保证容器安全是一项复杂的任务 这个问题域很广 面对大量的检查清单和最佳实践 你很难确定采用哪个解决方案 所以 如果你要实现容器安全策略 应该从哪里开始呢 我建议从最基本的开始 理解容器安全
  • v-model.number的坑,自动清除小数点后的0

  • Android7.0 获取蓝牙设备电量

    参考http blog csdn net jcxxxxx55 article details 52847291 locationNum 4 fps 1 1 修改 HeadsetStateMachine packages apps Bluet
  • 有趣的数据结构算法16——线索二叉树的构建

    有趣的数据结构算法16 线索二叉树的构建 什么是线索二叉树 线索二叉树的实现形式 线索二叉树的代码实现 线索二叉树的初始化 线索的串联 全部实现代码 GITHUB下载连接 深度遍历不仅仅有递归的方法噢 还有通过建立线索二叉树进行遍历的方法