线索二叉树(前序,中序,后序线索化以及遍历)

news2024/11/28 23:38:48

其实在这之前我想了很久,这个线索化二叉树我个人感觉是比实现二叉链表要难,很抽象的一个东西。好了,话先不多说,老规矩,先上代码:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int type;
typedef struct leadtree
{
	type val;
	int ltag;
	int rtag;
	struct leadtree* left;
	struct leadtree* right;
}leadtree;
void creat(leadtree** root);
void leadfront(leadtree** root);
void leadmid(leadtree** root);
void leadback(leadtree** root);
void traversefront(leadtree** root);
void traversemid(leadtree** root);
void traverseback(leadtree** root);
#define _CRT_SECURE_NO_WARNINGS 1
#include "leadh.h"
leadtree* pre = NULL;
void creat(leadtree** root)
{
	//这里强调一下,这个断言指的传进来的指向根节点的指针,如果都没有指向根节点的指针,就报错
	assert(root);
	printf("是否要继续生成左孩子还是右孩子->0,1\n");
	int ret = 0;
	scanf("%d", &ret);
	if (0 == ret)
	{
		*root = NULL;
		return;
	}
	else
	{
		leadtree* tem = (leadtree*)calloc(1, sizeof(leadtree));
		assert(tem);
		*root = tem;
		printf("请输入你要保存的值->\n");
		scanf("%d", &(*root)->val);
		creat(&(*root)->left);
		creat(&(*root)->right);
	}
}
static void visit(leadtree** root)
{
	if ((*root)->left == NULL)
	{
		(*root)->left = pre;
		(*root)->ltag = 1;
	}
	if (pre != NULL && pre->right == NULL)
	{
		pre->right = *root;
		pre->rtag = 1;
	}
	pre = *root;
}
static void front(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		visit(root);
		if ((*root)->ltag == 0)
			front(&(*root)->left);
		if ((*root)->rtag == 0)
			front(&(*root)->right);
	}
}
static void mid(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		mid(&(*root)->left);
		visit(root);
		mid(&(*root)->right);
	}
}
static void back(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		back(&(*root)->left);
		back(&(*root)->right);
		visit(root);
	}
}
//前序线索化二叉树
void leadfront(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
	{
		front(root);
		pre->rtag = 1;
	}
}
//中序线索化二叉树
void leadmid(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
	{
		mid(root);
		pre->rtag = 1;
	}
}
//后序线索化二叉树
void leadback(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
		back(root);
}
//前序线索化找后继
static leadtree* next(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->rtag == 1)
		return (*root)->right;
	else
	{
		if ((*root)->ltag != 1)
		{
			*root = (*root)->left;
			return *root;
		}
		else
		{
			*root = (*root)->right;
			return *root;
		}
	}
}
//遍历前序线索化
void traversefront(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = *root; tem != NULL; tem = next(&tem))
			printf("%d->", tem->val);
	}
}
//中序线索化找后继
static leadtree* nextmid(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->rtag == 1)
		return (*root)->right;
	else
	{
		if ((*root)->rtag != 1)
		{
			*root = (*root)->right;
			while ((*root)->ltag == 0)
				*root = (*root)->left;
			return *root;
		}
	}
}
static leadtree* first(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else
	{
		if ((*root)->ltag != 1)
		{
			while ((*root)->ltag == 0)
				*root = (*root)->left;
			return *root;
		}
		else
			return *root;
	}
}
//遍历中序线索化二叉树
void traversemid(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = first(root); tem != NULL; tem = nextmid(&tem))
			printf("%d->", tem->val);
	}
}
//后序线索化二叉树找前驱
static leadtree* prev(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->ltag == 1)
		return (*root)->left;
	else
	{
		if ((*root)->rtag != 1)
		{
			*root = (*root)->right;
			while ((*root)->rtag == 0)
				*root = (*root)->right;
			return *root;
		}
		else
		{
			*root = (*root)->left;
			while ((*root)->rtag == 0)
				*root = (*root)->right;
			return *root;
		}
	}
}
//遍历后序线索二叉树
void traverseback(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = *root; tem != NULL; tem = prev(&tem))
			printf("%d->", tem->val);
	}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include "leadh.h"
void test1()
{
	leadtree* root;
	creat(&root);
	leadfront(&root);
	//leadmid(&root);
	//leadback(&root);
	traversefront(&root);
	//traversemid(&root);
	//traverseback(&root);
}
int main()
{
	test1();
	return 0;
}

还是老样子,三个文件,就不一一说了。

我们可以看到的是,线索化二叉树的一个节点比二叉树的一个节点要多两个域,这两个域其实就是标记,线索化二叉树其实说起来很简单,就是利用它的前中后序遍历来边线索化,边边历,听起来也很简单,感觉没什么,但是这个在我们用代码实现的时候就不是这么简单了,因为你一不小心就很造成死循环。

先来看看下面的图:

这个就是手画的线索化完之后的二叉树,手画很简单,就是指针指向他的遍历序列的前驱,后指针指向遍历序列的后继。

现在我们说一下代码实现的思路以及注意事项:

首先,就是头文件,我相信没有什么要说的了。大家应该都是懂的。

其次,就是写函数的那个文件了,我先写的是前序线索化二叉树,但是在这里我先给大家说一下中序线索化二叉树,因为前序线索化二叉树有坑,先说中序线索化二叉树。其实也很简单,就是先遍历左子树,在线索化,线索化完当前的节点之后,在遍历右子树,就是这样。而具体的线索化过程在visit这个函数里面,具体不说了,(左指针一般都是指向前驱,右指针一般都是指向后继) 但是大家仔细的想一下,当我们用前序线索化二叉树的时候就会出现问题。什么问题呢?就是要在遍历他的左右子树之前,要判断一下,是否指向的是自己的子树。如果是,则遍历,如果不是,就不遍历,要不然会造成死循环(下面会详细讲)。

其次就是前序和中序线索化完之后,因为pre这个指针一定在树的最右面的那个节点,所以此时pre->right一定会是NULL,这个是因为我们在创建二叉树的时候,停止生成左右孩子的时候,我们就把这个节点赋值成了NULL,但是在线索化二叉树中,空指针就应该是指向的线索,所以此时我们不要多此一举,直接在他线索化完之后,直接就把他的右标记域改成1就好。

然后很多小伙伴可能有些疑惑,就是为什么这次创建二叉树的时候,为什么开辟空间不用malloc,而用了calloc,这个是因为有了两个标记域,所以一定要把标记与给初始化为0,可能又有些人说,就算不初始化,就算他是“垃圾值”,我们最后线索化的时候就把他改成1了,不影响,但是你仔细想一下,他只能改的是空指针域,那些没有空的指针域怎么办?是不是还是“垃圾值”,这样在遍历线索二叉树的时候怎么办?怎么找他的后继节点?所以,我们就用了calloc来开辟空间,开辟好空间直接就把左右标记赋值为0。

其次就是前序线索化的时候,很多人会疑惑,为什么形成死循环,但是我们用再用他遍历普通的二叉树的时候,为什么就不会形成死循环,来看看下面这幅图:

根据上面的图,我们可以看到,4的直接前驱是2,但是如果在线索化的时候,不根据他的左右标记来判断,就很在4的这里形成死循环,因为此时4指向了2,线索化完4,访问他的左指针域,就直接到了2,所以这样形成了死循环。所以一定要注意这个。中序和后序就没有此问题。

再就是在寻找前驱和后继的时候,前序线索化不好找前驱,后线索化不好找后继,此时就需要重新比那里二叉树来找他的前驱和后继。

以上就是这篇文章的内容,如果对你有用的话,就点一下赞吧!!!!谢谢支持!! 

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

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

相关文章

std::inner_product与std::accumulate初始值设置踩坑

std::inner_product函数可以用于计算两个序列的内积。在这个函数中&#xff0c;我们需要传递四个参数&#xff1a;两个源序列的起始迭代器、一个初始值和一个二元函数对象。 std::inner_product函数将对两个源序列中的每个元素进行乘法运算&#xff0c;并将结果累加到初始值中…

FPGA基于XDMA实现PCIE X4通信方案 提供工程源码和QT上位机程序和技术支持

目录1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI …

桥接设计模式解读

目录 问题引进 手机操作问题 传统方案解决手机操作问题 传统方案解决手机操作问题分析 桥接模式(Bridge)概述 基本介绍 原理类图 主要角色 桥接模式解决手机操作问题 桥接模式的注意事项和细节 常见的应用场景 问题引进 手机操作问题 现在对不同手机类型的不同品牌…

Mybatis(七)Mybatis的日志体系

在介绍Mybatis日志实现前&#xff0c;我们先了解下java的日志体系以及日志框架的发展&#xff0c;目前比较常用的日志框架有下面几个&#xff1a; 而JCL和SLF4J属于日志接口&#xff08;没有日志具体实现&#xff09;&#xff0c;提供统一的日志操作规范&#xff0c;而日志的实…

Halo博客建站实战以及问题汇总

目录 简介 特性 快速开始 安装步骤 环境准备 Docker-compose方式部署 问题汇总 mac端无法访问页面 页面登录提示账号密码错误 重装注意点 资料 官方文档 简介 Halo 强大易用的开源建站工具 特性 代码开源 我们的所有代码开源在 GitHub 上且处于积极维护状态&…

stata数据处理

stata数据处理–潘登同学的stata笔记 文章目录stata数据处理--潘登同学的stata笔记数据导入、导出使表格第一行成为变量标注(label)数据合并横向与纵向合并一对多与多对一横向合并缺失值处理查看缺失值替换缺失值缺失值填充离群值处理取对数的方法截尾处理、缩尾处理分组统计列…

银行数字化转型导师坚鹏:银行数字化转型痛点、路径与对策

银行数字化转型痛点、路径与对策课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不知道银行数字化转型&#xff1f; 不清楚数字化转型对银行发展有什么影响&#xff1f; 不知道数字化转型对银行有什么机遇&#xff1f; 学员收获: 学习银行数字化转型的发展现状与成…

2021四川省icpc省赛H题 Nihongo wa Muzukashii Desu 日本語は難しいです!

日本語は難しいです&#xff01; それは難しくないと思うだけど 传送门 一些吐槽 这题好像恶心了不少学弟啊 其实只要读懂了题就很好做, 对于我这种日语高考生来说确实是有点犯规了不过当时我写的时候不能用翻译一看题面一大串英文就被我pass掉, 后面看到一大堆人过了我才去写…

迅为RK3568开发平台人工智能人脸识别全国产解决方案赋能NVR

人脸识别服务&#xff0c;能够在图像中快速检测人脸、分析人脸关键点信息、获取人脸属性、实现人脸的精确比对和检索。可应用于身份验证、电子考勤、客流分析等场景人脸检测、比对、搜索&#xff0c;人脸库管理等。 为什么使用它呢&#xff1f; 维持公众聚集场所的秩序 在餐厅…

Apache的配置与运用

1. web虚拟主机的构建 虚拟Web主机指的是在同一台服务器中运行多个Web站点&#xff0c;其中每一个站点实际上并不独立占用整个服务器&#xff0c;因此被称为“虚拟”Web 主机。通过虚拟 Web 主机服务可以充分利用服务器的硬件资源&#xff0c;从而大大降低网站构建及运行成本。…

2.3.2 单链表的插入结点操作

--单链表示意图 一、按位序插入&#xff08;带头结点&#xff09; ①表头插入 注意点 1.i1&#xff0c;while循环不执行 2.时间复杂度为O&#xff08;1&#xff09; 3.p&#xff01;NULL表示指针p有指向结点 4.头结点为第0个结点 5.pL表示指针p和L指向位置相同&#xff0…

ORB_SLAM3_优化方法 Pose优化

PoseOptimization PoseOptimization主要的作用是利用重投影优化单帧的位姿,主要用在Tracking的几种跟踪模式TrackWithMotionModel、TrackReferenceKeyFrame、 TrackLocalMap、Relocalization中 输入 优化变量观测帧的Pose帧的MapPoint帧的KeyPoint初始化 //创建优化器 g2o…

4.Java逻辑控制语句

Java逻辑控制语句 在实际生活中&#xff0c;我们的生活不是一成不变的&#xff0c;很多时候需要我们去选择&#xff0c;大到人生的十字路口&#xff0c;小到今天晚上吃什么&#xff0c;选择无处不在。小的选择决定了我们一件小事的走向&#xff0c;大的选择可能会改变我们人生…

大模型“云上经济”之权力游戏

文丨谭婧光阴者百代之过客。世人皆过客&#xff0c;软件亦如此。人工智能已有新旧之分。网友戏称&#xff0c;新人工智能是ChatGPT&#xff0c;旧人工智能是“之前那些”。历史在重演。云计算在美国问世的时候&#xff0c;否定论调居多。一个常见的否定论调是「做生意不用上云」…

vite+vue3使用UEditorPlus ,后端PHP

vitevue3使用UEditorPlus什么是UEditorPlus功能亮点前端安装安装vue-ueditor-wrap3.x下载 UEditorPlus在main.js注册组件v-model 绑定数据后端配置效果百度富文本编辑器是目前所有编辑器中功能最丰富的&#xff0c;但长时间不进行维护了。之前写了一篇使用UEditor的教程&#x…

回调函数含义查询云记debug调试

回调函数 同步回调函数 回调函数通常就是当父函数执行完后&#xff0c;再执行通过传参进来的函数&#xff0c;当然也可以不传参&#xff0c;直接在父函数内部调用回调函数 注&#xff1a;立即执行回调&#xff0c;执行完回调代码才会继续往下执行 function a(callback) {a…

力扣:两数之和(哈希表)

1、两数之和 1、问题描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。…

如何利用超级电容设计简单的不间断电源

如何利用超级电容设计简单的不间断电源 在许多应用中&#xff0c;电源电压无论在什么情况下都持续可用是很重要的。要确保这一点有时并不容易。一种新概念可以为设计极其紧凑的不间断电源提供一种优化解决方案。 问题: 在电源关键型应用中&#xff0c;如何更轻松地获得持续、…

【Android平板编程】远程Ubuntu服务器code-server编程写代码

文章目录前言1.ubuntu本地安装code-server2. 安装cpolar内网穿透3. 创建隧道映射本地端口4. 安卓平板测试访问5.固定域名公网地址5.结语前言 本次教程将在 Ubuntu 服务器环境下安装 code-server &#xff0c;并使用 Android 安卓平板远程 Ubuntu 服务&#xff0c;进行远程编程开…

Python pandas和numpy用法参考(转)

以下是转载&#xff1a;Python pandas用法 - 简书介绍 在Python中&#xff0c;pandas是基于NumPy数组构建的&#xff0c;使数据预处理、清洗、分析工作变得更快更简单。pandas是专门为处理表格和混杂数据设计的&#xff0c;而NumPy更适合处...https://www.jianshu.com/p/840ba1…