数据结构 | 线索二叉树

news2024/9/21 22:36:32

一、数据结构定义

/* 线索二叉树 */
typedef char ThreadType;
typedef struct ThreadNode {
	ThreadType data;
	struct ThreadNode* lchild, * rchild;
	int ltag, rtag; //左右线索标志
}ThreadNode, *ThreadTree;

二、方法概览

ThreadTree createTree(); //先序方法创建二叉树
void threaded(ThreadNode* q); //对当前结点进行线索化操作
void createInThreadTree(ThreadTree T); //中序线索化二叉树T
void inThread(ThreadTree T); //中序遍历二叉树,边遍历边线索化
void preThread(ThreadTree T); //先序遍历二叉树,边遍历边线索化
void postThread(ThreadTree T); //后序遍历二叉树,边遍历边线索化

void visit(ThreadNode* node); //对结点T的操作
ThreadNode* firstNode(ThreadNode* p); // 找到以P为根节点的子树中,中序遍历首先被遍历的节点
ThreadNode* lastNode(ThreadNode* p); // 找到以P为根节点的子树中,中序遍历最后被遍历的节点
ThreadNode* nextNode(ThreadNode* p); // 在中序线索二叉树中找到P的后继节点
ThreadNode* preNode(ThreadNode* p); // 在中序线索二叉树中找到P的前驱节点
void inOrder_thread(ThreadNode* T); // 中序遍历二叉树(利用线索实现的非递归算法,空间复杂度O(1))
void revInOrder_thread(ThreadNode* T); // 逆向中序遍历二叉树

三、方法详解

/*-------- 线索化二叉树 --------*/
//先序方法创建二叉树
ThreadTree createTree() {
	ThreadTree root;
	ThreadType data;
	scanf("%c", &data);

	if (data == '#') root = NULL;
	else {
		root = (ThreadNode*)malloc(sizeof(ThreadNode));
		root->data = data;
		root->ltag = root->rtag = 0;
		root->lchild = createTree();
		root->rchild = createTree();
	}
	return root;
}
//对当前结点进行线索化操作
void threaded(ThreadNode* p) {
	// 左指针为空,建立前驱线索
	if (p->lchild == NULL) {
		p->lchild = pre;
		p->ltag = 1;
	}
	// 建立前驱节点的后继线索
	if (pre != NULL && pre->rchild == NULL) {
		pre->rchild = p;
		pre->rtag = 1;
	}
	// 指针后移
	pre = p;
}
//中序线索化二叉树T
void createInThreadTree(ThreadTree T) {
	pre = NULL;
	if (T != NULL) {
		inThread(T);
		if (pre->rchild == NULL)
			pre->rtag = 1;
	}
}
//中序遍历二叉树,边遍历边线索化
void inThread(ThreadTree T) {
	if (T != NULL) {
		inThread(T->lchild);
		threaded(T);
		inThread(T->rchild);
	}
}
//先序遍历二叉树,边遍历边线索化
void preThread(ThreadTree T) {
	if (T != NULL) {
		threaded(T);
		// lchild不是前驱线索时才访问其左孩子
		// 不加该条件,可能会出现两个指针无限循环打转
		if(T->ltag==0) 
			preThread(T->lchild);
		preThread(T->rchild);
	}
}
//后序遍历二叉树,边遍历边线索化
void postThread(ThreadTree T) {
	if (T != NULL) {
		// 不会出现先序遍历的情况
		// 因为进行 visit(T) 的时候,其左孩子、右孩子已经处理完,不会再访问了
		postThread(T->lchild);
		postThread(T->rchild);
		threaded(T);
	}
}

/*-------- 遍历线索二叉树 --------*/
//对结点T的操作
void visit(ThreadNode* node) {
	printf("%c ", node->data);
}
// 找到以P为根节点的子树中,中序遍历首先被遍历的节点
ThreadNode* firstNode(ThreadNode* p) {
	//循环找到中序遍历的最左下角的节点(不一定是叶子节点)
	while (p->ltag == 0) p = p->lchild;
	return p;
}
// 找到以P为根节点的子树中,中序遍历最后被遍历的节点
ThreadNode* lastNode(ThreadNode* p) {
	//循环找到中序遍历的最右下角的节点(不一定是叶子节点)
	while (p->rtag == 0) p = p->rchild;
	return p;
}
// 在中序线索二叉树中找到P的后继节点
ThreadNode* nextNode(ThreadNode* p) {
	// p->rtag = 0(指示P节点的左孩子),返回右子树最左下节点
	if (p->rtag == 0) return firstNode(p->rchild);
	// p->rtag = 1(指示线索),直接返回后继线索
	else return p->rchild;
}
// 在中序线索二叉树中找到P的前驱节点
ThreadNode* preNode(ThreadNode* p) {
	// p->rtag = 0(指示P节点的右孩子),返回左子树最右下节点
	if (p->ltag == 0) return lastNode(p->lchild);
	// p->rtag = 1(指示线索),直接返回前驱线索
	else return p->lchild;
}
// 中序遍历二叉树(利用线索实现的非递归算法,空间复杂度O(1))
void inOrder_thread(ThreadNode* T) {
	for (ThreadNode* p = firstNode(T); p != NULL; p = nextNode(p)) {
		visit(p);
	}
}
// 逆向中序遍历二叉树
void revInOrder_thread(ThreadNode* T) {
	for (ThreadNode* p = lastNode(T); p != NULL; p = preNode(p)) {
		visit(p);
	}
}

四、运行结果

        main方法代码如下:

int main() {
	// 输入先序序列 ABD##E#H##CF##G## 创建二叉树
	printf("输入先序序列创建二叉树:");
	ThreadTree root = createTree();
	createInThreadTree(root);

	printf("\n中序遍历:");
	inOrder_thread(root);
	printf("\n逆向中序遍历:");
	revInOrder_thread(root);
}

        运行结果如下:

 

五、源代码

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

/* 线索二叉树 */
typedef char ThreadType;
typedef struct ThreadNode {
	ThreadType data;
	struct ThreadNode* lchild, * rchild;
	int ltag, rtag; //左右线索标志
}ThreadNode, *ThreadTree;


ThreadTree createTree(); //先序方法创建二叉树
void threaded(ThreadNode* q); //对当前结点进行线索化操作
void createInThreadTree(ThreadTree T); //中序线索化二叉树T
void inThread(ThreadTree T); //中序遍历二叉树,边遍历边线索化
void preThread(ThreadTree T); //先序遍历二叉树,边遍历边线索化
void postThread(ThreadTree T); //后序遍历二叉树,边遍历边线索化

void visit(ThreadNode* node); //对结点T的操作
ThreadNode* firstNode(ThreadNode* p); // 找到以P为根节点的子树中,中序遍历首先被遍历的节点
ThreadNode* lastNode(ThreadNode* p); // 找到以P为根节点的子树中,中序遍历最后被遍历的节点
ThreadNode* nextNode(ThreadNode* p); // 在中序线索二叉树中找到P的后继节点
ThreadNode* preNode(ThreadNode* p); // 在中序线索二叉树中找到P的前驱节点
void inOrder_thread(ThreadNode* T); // 中序遍历二叉树(利用线索实现的非递归算法,空间复杂度O(1))
void revInOrder_thread(ThreadNode* T); // 逆向中序遍历二叉树

//指向当前访问节点的前驱
ThreadNode* pre = NULL;

/*-------- 线索化二叉树 --------*/
//先序方法创建二叉树
ThreadTree createTree() {
	ThreadTree root;
	ThreadType data;
	scanf("%c", &data);

	if (data == '#') root = NULL;
	else {
		root = (ThreadNode*)malloc(sizeof(ThreadNode));
		root->data = data;
		root->ltag = root->rtag = 0;
		root->lchild = createTree();
		root->rchild = createTree();
	}
	return root;
}
//对当前结点进行线索化操作
void threaded(ThreadNode* p) {
	// 左指针为空,建立前驱线索
	if (p->lchild == NULL) {
		p->lchild = pre;
		p->ltag = 1;
	}
	// 建立前驱节点的后继线索
	if (pre != NULL && pre->rchild == NULL) {
		pre->rchild = p;
		pre->rtag = 1;
	}
	// 指针后移
	pre = p;
}
//中序线索化二叉树T
void createInThreadTree(ThreadTree T) {
	pre = NULL;
	if (T != NULL) {
		inThread(T);
		if (pre->rchild == NULL)
			pre->rtag = 1;
	}
}
//中序遍历二叉树,边遍历边线索化
void inThread(ThreadTree T) {
	if (T != NULL) {
		inThread(T->lchild);
		threaded(T);
		inThread(T->rchild);
	}
}
//先序遍历二叉树,边遍历边线索化
void preThread(ThreadTree T) {
	if (T != NULL) {
		threaded(T);
		// lchild不是前驱线索时才访问其左孩子
		// 不加该条件,可能会出现两个指针无限循环打转
		if(T->ltag==0) 
			preThread(T->lchild);
		preThread(T->rchild);
	}
}
//后序遍历二叉树,边遍历边线索化
void postThread(ThreadTree T) {
	if (T != NULL) {
		// 不会出现先序遍历的情况
		// 因为进行 visit(T) 的时候,其左孩子、右孩子已经处理完,不会再访问了
		postThread(T->lchild);
		postThread(T->rchild);
		threaded(T);
	}
}

/*-------- 遍历线索二叉树 --------*/
//对结点T的操作
void visit(ThreadNode* node) {
	printf("%c ", node->data);
}
// 找到以P为根节点的子树中,中序遍历首先被遍历的节点
ThreadNode* firstNode(ThreadNode* p) {
	//循环找到中序遍历的最左下角的节点(不一定是叶子节点)
	while (p->ltag == 0) p = p->lchild;
	return p;
}
// 找到以P为根节点的子树中,中序遍历最后被遍历的节点
ThreadNode* lastNode(ThreadNode* p) {
	//循环找到中序遍历的最右下角的节点(不一定是叶子节点)
	while (p->rtag == 0) p = p->rchild;
	return p;
}
// 在中序线索二叉树中找到P的后继节点
ThreadNode* nextNode(ThreadNode* p) {
	// p->rtag = 0(指示P节点的左孩子),返回右子树最左下节点
	if (p->rtag == 0) return firstNode(p->rchild);
	// p->rtag = 1(指示线索),直接返回后继线索
	else return p->rchild;
}
// 在中序线索二叉树中找到P的前驱节点
ThreadNode* preNode(ThreadNode* p) {
	// p->rtag = 0(指示P节点的右孩子),返回左子树最右下节点
	if (p->ltag == 0) return lastNode(p->lchild);
	// p->rtag = 1(指示线索),直接返回前驱线索
	else return p->lchild;
}
// 中序遍历二叉树(利用线索实现的非递归算法,空间复杂度O(1))
void inOrder_thread(ThreadNode* T) {
	for (ThreadNode* p = firstNode(T); p != NULL; p = nextNode(p)) {
		visit(p);
	}
}
// 逆向中序遍历二叉树
void revInOrder_thread(ThreadNode* T) {
	for (ThreadNode* p = lastNode(T); p != NULL; p = preNode(p)) {
		visit(p);
	}
}


int main() {
	// 输入先序序列 ABD##E#H##CF##G## 创建二叉树
	printf("输入先序序列创建二叉树:");
	ThreadTree root = createTree();
	createInThreadTree(root);

	printf("\n中序遍历:");
	inOrder_thread(root);
	printf("\n逆向中序遍历:");
	revInOrder_thread(root);
}

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

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

相关文章

==和equals的区别

“”和equals 最大的区别是 “”是运算符&#xff0c;如果是基本数据类型&#xff0c;则比较存储的值&#xff1b;如果是引用数据类型&#xff0c;则比较所指向对象的地址值。equals是Object的方法&#xff0c;比较的是所指向的对象的地址值&#xff0c;一般情况下&#xff0c…

Jvisualvm内存模型剖析-JVM(五)

上篇文章代码讲解了tomcat加载以及gc回收流程。 Jvm内存模型剖析优化-JVM&#xff08;四&#xff09; Jvisualvm 我们可以编写如上代码&#xff0c;之后打开jvm自带的工具jvisualvm。 如果我们看visual不会明显变化&#xff0c;则可以修改睡眠参数&#xff0c;时间改小。 当…

【MySQL】根据MVCC和Read View分析事务的四种隔离级别在读写场景分别是如何体现其隔离性的

目录 一、数据库并发的三种场景 二、读写场景的MVCC 1、3个&#xff08;4个&#xff09;记录隐藏列字段 2、undo log&#xff08;撤销日志&#xff09; 3、模拟MVCC场景 3.1update场景 3.2delete场景 3.3insert 3.4select场景 4、Read View 5、RR和RC的区别 5.1当…

Squid 代理服务器应用

目录 一、Squid 概念1.1 代理的工作机制1.2 代理服务器的作用1.3 Squid 代理的类型 二、安装 Squid 服务2.1 编译安装 Squid2.2 修改 Squid 的配置文件2.3 Squid 的运行控制2.4 创建 Squid 服务脚本 三、构建传统代理服务器四、构建透明代理 服务器五、ACL 访问控制六、 日志分…

Python——模块与包

一、模块 &#xff08;1&#xff09;模块的定义 模块——Modules,通常一个py文件就是一个模块&#xff0c;我们在一个py文件里面会定义多个函数&#xff0c;也就是说一个模块中可以包含N多个函数。 &#xff08;2&#xff09;模块化编程的好处 ①方便其他程序和脚本的导入并使用…

从0到1精通自动化测试,pytest自动化测试框架,allure描述用例详细讲解(二十二)

一、前言 pytestallure是最完美的结合了&#xff0c;关于allure的使用&#xff0c;本篇做一个总结。 allure报告可以很多详细的信息描述测试用例&#xff0c;包括epic、feature、story、title、issue、testcase、severity等 环境准备&#xff1a; 1.python 3.6 2.pytest 4.5.…

使用Jetpack Compose创建滑动刷新(SwipeRefreshLayout)

Compose并未像View-based系统那样内建SwipeRefreshLayout。但Compose鼓励你自行创建需要的可组合函数&#xff0c;它提供了足够的工具供你完成此任务。 在本篇博客中&#xff0c;我们将展示如何在Jetpack Compose中创建滑动刷新界面的过程。但请注意&#xff0c;以下代码相当简…

flutter:实现一个简单的appBar上的搜索框、一个简单的搜索历史

搜索框 效果图 代码 import package:flutter/material.dart;class NovelSearch extends StatefulWidget {overrideState<StatefulWidget> createState() > _NovelSearchState(); }class _NovelSearchState extends State<NovelSearch> {String searchVal ;o…

DSP的CLA编程及注意事项之一

CLA简介 CLA(Control Law Accelerator),即控制律加速器&#xff0c;该 CLA 是完全可编程的独立 32 位浮点 CPU&#xff0c;专为优化数学密集型计算而设计&#xff0c;可显著提升控制算法的性能。与 执行指令和处理中断的标准传统处理器不同&#xff0c;CLA 实际上是任务驱动状…

Axure教程—上传文件

本文介绍用Axure制作文件上传效果 预览 预览地址&#xff1a;https://6q4of2.axshare.com 功能 1、点击”文件上传“按钮&#xff0c;显示上传的文件 2、点击”删除“图片&#xff0c;显示提示”是否要删除“&#xff0c;点击”是“&#xff0c;删除数据&#xff0c;点击”否…

开放式蓝牙耳机好不好,列举出几款值得入手的开放式蓝牙耳机

开放式耳机不仅能够提升幸福感还能听到周围环境声&#xff0c;大大提高安全性&#xff0c;不入耳不伤耳设计&#xff0c;既稳固又舒适&#xff0c;佩戴上耳无压力&#xff0c;还具有良好的音质和舒适的佩戴体验。但市面开放式耳机质量也参差不齐&#xff0c;有些使用感不佳&…

高压放大器使用说明书

高压放大器是一种电子设备&#xff0c;可以将输入信号的电能转换成输出信号的电能&#xff0c;从而实现信号放大的功能。它广泛应用于各种领域&#xff0c;例如通信、雷达、医疗等等。下面是一份高压放大器使用说明书&#xff0c;帮助用户更好地了解和使用该设备。 一、高压放大…

追思郭文彬:不管封我当什么长,下辈子我还当厨师!

6月28日&#xff0c;在北京联合大学旅游学院餐饮管理系的走廊&#xff0c;大大的长条桌上摆放着各类像生面点、面塑、包子等美食作品——牡丹、玉兰花、灯笼、小金鱼、花生、龙眼、山竹、翡翠白菜……作品精巧细致&#xff0c;形神兼备&#xff0c;宛若工艺品。 这些作品都出自…

MyBatis-Plus 实现PostgreSQL数据库jsonb类型的保存与查询

文章目录 在 handle 包下新建Jsonb处理类方式一方式二 PostgreSQL jsonb类型保存新建数据库表含有jsonb类型创建实体类Control创建保存数据库方法发起请求 PostgreSQL jsonb类型查询Control创建查询数据库方法发起请求 在 handle 包下新建Jsonb处理类 方式一 import com.alib…

(css)el-image图片完整显示,不拉伸收缩

(css)el-image图片完整显示&#xff0c;不拉伸收缩 <el-imagefit"contain" //重要设置src"../../../../1.png"altclass"chenguo_img_img" />

【RocketMQ】CentOS8安装RocketMQ

RocketMQ的安装 检查jdk环境 RocketMQ是基于java开发的&#xff0c;安装之前请先查看是否有jdk环境 java -version如果没有请去官网&#xff08;https://www.oracle.com/java/technologies/downloads/#java8&#xff09;下载 下载RocketMQ安装包 前往官网&#xff08;http…

信道编码:Matlab RS编码、译码使用方法

Matlab RS编码、译码使用方法 1. 相关函数 在MATLAB中进行RS编码的过程可以使用rsenc()函数或者comm.RSEncoder()函数。 1.1 rsenc()函数使用方法 在MATLAB中帮助中可以看到有三种使用形式&#xff0c;分别为 code rsenc(msg,n,k) code rsenc(msg,n,k,genpoly) code rs…

Excel拼接sql语句,批量导入数据

如下示例图&#xff1a; sql语句&#xff1a;"insert into table (code, path, method) values ("&A2&"’,"&B2&","&C2&"’’);" "("&A2&","&B2&","&C2…

数据分析Lambda架构详解

大家好&#xff0c;今天我们来介绍一个用于亿级实时数据分析架构Lambda架构。 Lambda架构 Lambda架构&#xff08;Lambda Architecture&#xff09;是由Twitter工程师南森马茨&#xff08;Nathan Marz&#xff09;提出的大数据处理架构。这一架构的提出基于马茨在BackType和Tw…

形式化验证,A Theoretical Framework for SymbolicQuick Error Detection(四)

一、Article:文献出处&#xff08;方便再次搜索&#xff09; &#xff08;1&#xff09;作者 Florian Lonsing, Subhasish Mitra, and Clark Barrett&#xff08;Stanford University&#xff0c;斯坦福大学&#xff09; &#xff08;2&#xff09;文献题目 A Theoretical F…