前言

在《大话数据结构》P190 页中有一句话:其实线索二叉树,就等于是把一棵二叉树转变成了一个双向链表。

对于这句话实在想不懂,线索二叉树只是把二叉树以某种次序遍历把空域填上前驱或后继而已,若度为 2 的结点没有多余的指针域用于线索了,那双向链表就断了啊。

李柱明博客:https://www.cnblogs.com/lizhuming/p/15487406.html

线索二叉树的概念

概念:

  • 引入原因:二叉链表中,只能看出左右孩子,而看不出某序遍历的前驱和后继。

  • 线索:指向前驱和后继的指针称为线索。

  • 线索链表:加上线索的二叉链表称为线索链表。

  • 线索二叉树(Threaded Binary Tree):加上线索的二叉树称为线索二叉树。

    • 左域为空则改为前驱。
    • 右域为空则改为后继。
    • 每个节点添加左右 tag,用于标记左右域指针是孩子还是线索。
  • 线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。

    • 前、中、后序线索二叉树。

线索二叉树的实现

前驱和后继的信息只有在遍历二叉树时才能得到。而在遍历二叉树时便可以线索化。

线索化:

  • 在遍历过程中修改空指针。

  • 空左域为前驱。

  • 空右域为后继。

  • 实际实现:

    • 前、中、后序:正常的中序遍历算法。在遍历过程中,把输出数据部分改为线索化内容,即是修改空指针。

线索二叉树的遍历:

  • 中序:采用非递归方式:

    • 中序遍历算法思路:先找到最左下,再中,再切到右子树找到右子树的最左下,再中,再右...。

      • 简单来说就是:左、中、右。先找最左下。切到右时也要先找右的最左下。
    • 实现步骤:

      1. 找最左下:一直循环找左孩子,直到找到 ltag 为线索的节点。

      2. 找到最左下节点后,输出数据。

      3. 判断该节点是否有后继。

        1. 有:则直接跳到后继节点。输出数据。判断该节点是否有后继(就是重复步骤 3)。(线索)
        2. 无:则切到右孩子,对该右子树做前序遍历。即是找这个右孩子的最左下(重复步骤 1 、2、3)。(孩子)
        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 所指结点的后继点的方法:

  1. 若 p->rtag==1,则 p->rchild 指向其后继节。

  2. 若 p->rtag==0,则 p 所指结点的中序后继必然是其右子树中进行中序遍历时得到的第一个结点。也就是 p 的右子树中“最左下”的结点。

    1. 这点可以利用递归。
    2. 也可以参考下面代码的非递归方法。

寻点代码参考下面代码。

类双向链表参考图

参考代码

中序遍历线索化

  • 注意: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. 数据结构算法及应用——二叉树

    一.二叉树性质 特性1 包含n (n> 0 )个元素的二叉树边数为n-1 特性2 二叉树的高度(height)或深度(depth)是指该二叉树的层数(有几层元素,而不是有层的元素间隔) 特性3 ...

  2. 【PHP数据结构】完全二叉树、线索二叉树及树的顺序存储结构

    在上篇文章中,我们学习了二叉树的基本链式结构以及建树和遍历相关的操作.今天我们学习的则是一些二叉树相关的概念以及二叉树的一种变形形式. 完全二叉树 什么叫完全二叉树呢?在说到完全二叉树之前,我们先说另 ...

  3. 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)

    本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...

  4. 数据结构《9》----Threaded Binary Tree 线索二叉树

    对于任意一棵节点数为 n 的二叉树,NULL 指针的数目为  n+1 , 线索树就是利用这些 "浪费" 了的指针的数据结构. Definition: "A binary ...

  5. 数据结构之线索二叉树——C语言实现

     线索二叉树操作 (1) 线索二叉树的表示:将每个节点中为空的做指针与右指针分别用于指针节点的前驱和后续,即可得到线索二叉树. (2) 分类:先序线索二叉树,中序线索二叉树,后续线索二叉树 (3) 增 ...

  6. javascript实现数据结构:线索二叉树

    遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树 ...

  7. 数据结构之---C语言实现线索二叉树

    //线索二叉树,这里在二叉树的基础上增加了线索化 //杨鑫 #include <stdio.h> #include <stdlib.h> typedef char ElemTy ...

  8. 笔试算法题(41):线索二叉树(Threaded Binary Tree)

    议题:线索二叉树(Threaded Binary Tree) 分析: 为除第一个节点外的每个节点添加一个指向其前驱节点的指针,为除最后一个节点外的每个节点添加一个指向其后续节点的指针,通过这些额外的指 ...

  9. 数据结构算法集---C++语言实现

    //数据结构算法集---C++语言实现 //各种类都使用模版设计,可以对各种数据类型操作(整形,字符,浮点) /////////////////////////// // // // 堆栈数据结构 s ...

随机推荐

  1. pyqt5设计无边框窗口(一)

    import sys from PyQt5 import QtGui,QtCore from PyQt5 import QtCore, QtGui, QtWidgets ############### ...

  2. CF280D-k-Maximum Subsequence Sum【模拟费用流,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/CF280D 题目大意 一个长度为\(n\)的序列,\(m\)次操作 修改一个数 询问一个区间中选出\(k\)段不交子 ...

  3. P4292-[WC2010]重建计划【长链剖分,线段树,0/1分数规划】

    正题 题目链接:https://www.luogu.com.cn/problem/P4292 题目大意 给出\(n\)个点的一棵树,然后求长度在\([L,U]\)之间的一条路径的平均权值最大. 解题思 ...

  4. Vite插件开发纪实:vite-plugin-monitor(下)

    前言 上一篇介绍了Vite启动,HMR等时间的获取. 但各阶段详细的耗时信息,只能通过debug的日志获取 本文就实现一下debug日志的拦截 插件效果预览 --debug做了什么 项目启动指令 vi ...

  5. 网络基础--简单理解什么是DNS? TCP? UDP? Http? Socket?

    什么是IP 协议?  协议就是为了实现网络通信而创建的一系列规范.  通常我们的网络模型从上到下共分为4层: 应用层, 传输层, 网络层 和数据链路层. IP协议属于网络层协议,它精确定义了网络通信中 ...

  6. 基于注解实现jackson动态JsonProperty

    基于注解实现jackson动态JsonProperty @JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,但是值是固定的 ...

  7. Java基础之(六):变量、运算符与JavaDoc

    变量.常量 一.变量的命名规范 首字母只能以字母(A-Z或者a-z)或者美元符($)或者下划线(_)开头,不能以数字开头,首字母之后只能跟字母(AZ或者az)或者数字,不能跟美元符或者下划线 源码 p ...

  8. k8s学习笔记(2)- Rancher2.x部署springboot应用及高可用、扩容

    前言:上一篇介绍基于k3s环境,使用kubectl部署springboot简单项目应用,本篇介绍基于rancher2.x部署应用程序 1.上篇已部署一个springboot应用,我们可以通过ranch ...

  9. 2021年1月-第02阶段-前端基础-HTML+CSS进阶-VS Code 软件

    软件安装 VSCode软件 能够安装 VS Code 能够熟练使用 VS Code 软件 能够安装 VS Code 最常用的插件 1. VS Code简介 1.1 VS Code 简介 Visual ...

  10. 【数据结构】<栈的应用>回文判断

    通过栈与队列相关内容的学习,我们知道,栈是"先进后出"的线性表,而队列是"先进先出"的线性表.可以通过构造栈与队列来实现在这一算法.将要判断的字符序列依次压栈和 ...