【数据结构&算法】12-线索二叉树
前言
在《大话数据结构》P190 页中有一句话:其实线索二叉树,就等于是把一棵二叉树转变成了一个双向链表。
对于这句话实在想不懂,线索二叉树只是把二叉树以某种次序遍历把空域填上前驱或后继而已,若度为 2 的结点没有多余的指针域用于线索了,那双向链表就断了啊。
李柱明博客:https://www.cnblogs.com/lizhuming/p/15487406.html
线索二叉树的概念
概念:
引入原因:二叉链表中,只能看出左右孩子,而看不出某序遍历的前驱和后继。
线索:指向前驱和后继的指针称为线索。
线索链表:加上线索的二叉链表称为线索链表。
线索二叉树(Threaded Binary Tree):加上线索的二叉树称为线索二叉树。
- 左域为空则改为前驱。
- 右域为空则改为后继。
- 每个节点添加左右 tag,用于标记左右域指针是孩子还是线索。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
- 前、中、后序线索二叉树。
线索二叉树的实现
前驱和后继的信息只有在遍历二叉树时才能得到。而在遍历二叉树时便可以线索化。
线索化:
在遍历过程中修改空指针。
空左域为前驱。
空右域为后继。
实际实现:
- 前、中、后序:正常的中序遍历算法。在遍历过程中,把输出数据部分改为线索化内容,即是修改空指针。
线索二叉树的遍历:
中序:采用非递归方式:
中序遍历算法思路:先找到最左下,再中,再切到右子树找到右子树的最左下,再中,再右...。
- 简单来说就是:左、中、右。先找最左下。切到右时也要先找右的最左下。
实现步骤:
找最左下:一直循环找左孩子,直到找到 ltag 为线索的节点。
找到最左下节点后,输出数据。
判断该节点是否有后继。
- 有:则直接跳到后继节点。输出数据。判断该节点是否有后继(就是重复步骤 3)。(线索)
- 无:则切到右孩子,对该右子树做前序遍历。即是找这个右孩子的最左下(重复步骤 1 、2、3)。(孩子)
- 提醒:左右域只有两种情况:1. 线索;2. 孩子。
线索二叉树节点:
/* 线索二叉树节点结构 */
typedef int tree_elem_type;
/* link_e==0表示指向左右孩子指针, */
/* thread_e==1表示指向前驱或后继的线索 */
typedef enum {link_e, thread_e} pointer_tag_e;
typedef struct tree_node
{
struct tree_node *lchild; // 左子域
struct tree_node *rchild; // 右兄域
pointer_tag_e ltag; // 左标志
pointer_tag_e rtag; // 右标志
tree_elem_type data; // 数据
}binary_tree_node_t;
线索化代码参考下面参考代码。
线索二叉树的寻点思路二
以中序线索二叉树为例,令 P 所指节点的某个结点。查找 p 所指结点的后继点的方法:
若 p->rtag==1,则 p->rchild 指向其后继节。
若 p->rtag==0,则 p 所指结点的中序后继必然是其右子树中进行中序遍历时得到的第一个结点。也就是 p 的右子树中“最左下”的结点。
- 这点可以利用递归。
- 也可以参考下面代码的非递归方法。
寻点代码参考下面代码。
类双向链表参考图

参考代码
中序遍历线索化
- 注意:
in_threading();函数使用前需要对 pre 节点进行赋有效节点值。 - 下面为类双向链表线索化和线索遍历参考代码
binary_tree_node_t *pre = NULL; /* 全局变量, 上一访问节点 */
/* 中序遍历进行中序线索化 */
void in_threading(binary_tree_node_t *p)
{
if(p)
{
in_threading(p->lchild); /* 递归左子树线索化 */
if(!p->lchild) /* 没有左孩子 */
{
p->ltag = thread_e; /* 前驱线索 */
p->lchild = pre; /* 左孩子指针指向前驱 */
}
if(!pre->rchild) /* 前驱没有右孩子 */
{
pre->rtag = thread_e; /* 后继线索 */
pre->rchild = p; /* 前驱右孩子指针指向后继(当前结点p) */
}
pre = p; /* 保持pre指向p的前驱 */
in_threading(p->rchild); /* 递归右子树线索化 */
}
}
/* 中序遍历二叉树tree,并将其中序线索化,tree_list指向头结点 */
int in_order_threading(binary_tree_node_t **tree_list_p, binary_tree_node_t *tree)
{
*tree_list_p = (binary_tree_node_t *)malloc(sizeof(binary_tree_node_t));
if(!*tree_list_p)
return -1;
(*tree_list_p)->ltag = link_e; /* 孩子,用于指根 */
(*tree_list_p)->rtag = thread_e; /* 线索,只最右下。即是链尾 */
(*tree_list_p)->rchild = (*tree_list_p); /* 初始化:右指针回指 */
if(!tree) /* 若二叉树空,则左指针回指 */
(*tree_list_p)->lchild = *tree_list_p;
else
{
(*tree_list_p)->lchild = tree;
pre = (*tree_list_p);
in_threading(tree); /* 中序遍历进行中序线索化 */
pre->rchild = *tree_list_p;
pre->rtag = thread_e; /* 最后一个结点线索化 */
(*tree_list_p)->rchild = pre;
}
return 0;
}
/* 中序遍历二叉线索树tree(头结点)的非递归算法 */
int in_order_traverse_tree_list(binary_tree_node_t *tree)
{
binary_tree_node_t *p = NULL;
p = tree->lchild; /* p指向根结点 */
while(p != tree)
{
/* 找到最左下节点 */
while(p->ltag == link_e)
p = p->lchild;
if(!visit(p->data)) /* 访问其左子树为空的结点 */
return -1;
/* 判断是否有后继,且不是整个树的最右下节点 */
while(p->rtag == thread_e && p->rchild != tree)
{
p = p->rchild;
visit(p->data); /* 访问后继结点 */
}
p = p->rchild; /* 切换到右子树。然后继续中序遍历该右子树 */
}
return -1;
}
【数据结构&算法】12-线索二叉树的更多相关文章
- 数据结构算法及应用——二叉树
一.二叉树性质 特性1 包含n (n> 0 )个元素的二叉树边数为n-1 特性2 二叉树的高度(height)或深度(depth)是指该二叉树的层数(有几层元素,而不是有层的元素间隔) 特性3 ...
- 【PHP数据结构】完全二叉树、线索二叉树及树的顺序存储结构
在上篇文章中,我们学习了二叉树的基本链式结构以及建树和遍历相关的操作.今天我们学习的则是一些二叉树相关的概念以及二叉树的一种变形形式. 完全二叉树 什么叫完全二叉树呢?在说到完全二叉树之前,我们先说另 ...
- 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)
本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...
- 数据结构《9》----Threaded Binary Tree 线索二叉树
对于任意一棵节点数为 n 的二叉树,NULL 指针的数目为 n+1 , 线索树就是利用这些 "浪费" 了的指针的数据结构. Definition: "A binary ...
- 数据结构之线索二叉树——C语言实现
线索二叉树操作 (1) 线索二叉树的表示:将每个节点中为空的做指针与右指针分别用于指针节点的前驱和后续,即可得到线索二叉树. (2) 分类:先序线索二叉树,中序线索二叉树,后续线索二叉树 (3) 增 ...
- javascript实现数据结构:线索二叉树
遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树 ...
- 数据结构之---C语言实现线索二叉树
//线索二叉树,这里在二叉树的基础上增加了线索化 //杨鑫 #include <stdio.h> #include <stdlib.h> typedef char ElemTy ...
- 笔试算法题(41):线索二叉树(Threaded Binary Tree)
议题:线索二叉树(Threaded Binary Tree) 分析: 为除第一个节点外的每个节点添加一个指向其前驱节点的指针,为除最后一个节点外的每个节点添加一个指向其后续节点的指针,通过这些额外的指 ...
- 数据结构算法集---C++语言实现
//数据结构算法集---C++语言实现 //各种类都使用模版设计,可以对各种数据类型操作(整形,字符,浮点) /////////////////////////// // // // 堆栈数据结构 s ...
随机推荐
- Nresource服务之接口缓存化
1. 背景 Nresource服务日均4.5亿流量,考虑到未来流量急增场景,我们打算对大流量接口进行缓存化处理:根据服务管理平台数据统计显示getUsableResoureCount接口调用量很大,接 ...
- Win10环境下多JDK切换以及could not find java.dll异常解决
备注:主要为JDK1.7和JDK1.8之间进行切换 1.每次进行JDK切换时,都需要修改JAVA_HOME 2.编辑path环境变量,如图所示,将%JAVA_HOME%\jre\bin和%JAVA_H ...
- Winform配置文件读写操作
前言 在项目当中为了增加软件的灵活性及可配置性,配置文件在程序当中起着不可替代的作用.下面介绍一下最近用的比较多的方式. config文件的操作 数据库连接字符串 1.获取连接字符串 public s ...
- serialVersionUID序列化版本号与ObjectOutputStream对象输入输出流
1. 观察ObjectOutputStream 我们观察ObjectOutputStream就可以发现该类没有无参构造,只有有参构造,所以他是一个包装流 2. 具体使用: public static ...
- Kronecker product
Kronecker product 的基本运算 结合律 \begin{equation} \mathrm{A} \otimes (\mathrm{B + C}) = \mathrm{A} \otime ...
- Docker-Compose的一些常用命令
一.Docker-Compose简介 1.Docker-Compose简介 Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排. Docker-Co ...
- Java编程思想 第九章 接口
第九章 接口 抽象类和抽象方法 抽象:从具体事物抽出.概括出它们共同的方面.本质属性与关系等,而将个别的.非本质的方面.属性与关系舍弃,这种思维过程,称为抽象. 这句话概括了抽象的概念,而在Java中 ...
- VS运行时 /MD、/MDd 和 /MT、/MTd之间的区别
程序运行时出现问题,选择的是Release,win64位的模式,并且已经看到了宏定义NDEBUG,但是程序依然进入上面的部分 解决方案是将属性->C/C++->代码生成器->运行库里 ...
- 2021MySQL 8.0.26安装教程,目前最新版(详细全面)
MySQL 8 要比 MySQL 5.7 快 2 倍,还带来了大量的改进和更快的性能!所以为什么不用MySQL8呢!下面是MySQL 8.0.26的安装教程. 安装网址: https://dev.my ...
- 洛谷3348 大森林 (LCT + 虚点 + 树上差分)
这可真是道神仙题QWQ问了好多\(dalao\)才稍微明白了一丢丢做法 首先,我们假设不存在\(1\)操作,那么对于询问的一段区间中的所有的树,他们的形态应该是一样的 甚至可以直接理解为\(0\)操作 ...