前言

在《大话数据结构》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. disruptor笔记之四:事件消费知识点小结

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. [gdoi2018 day1]小学生图论题【分治NTT】

    正题 题目大意 一张随机的\(n\)个点的竞赛图,给出它的\(m\)条相互无交简单路径,求这张竞赛图的期望强联通分量个数. \(1\leq n,m\leq 10^5\) 解题思路 先考虑\(m=0\) ...

  3. CF917D-Stranger Trees【矩阵树定理,高斯消元】

    正题 题目链接:https://www.luogu.com.cn/problem/CF917D 题目大意 给出\(n\)个点的一棵树,对于每个\(k\)求有多少个\(n\)个点的树满足与给出的树恰好有 ...

  4. Erase-Remove 惯用法

    看到<Effective STL>条款 9 的时候想到了我以前复习的"如何正确使用迭代器删除元素",我面试时使用的也是里面的方法,看面试官的反应好像也没有什么问题,还问 ...

  5. FastAPI(58)- 使用 OAuth2PasswordBearer 的简单栗子

    背景 假设在某个域中拥有后端 API(127.0.0.1:8080) 并且在另一个域或同一域的不同路径(或移动应用程序)中有一个前端(127.0.0.1:8081) 并且希望有一种方法让前端使用用户名 ...

  6. 题解 「BZOJ2178」圆的面积并

    题目传送门 题目大意 给出 \(n\) 个圆,求它们并的面积大小. \(n\le 10^3\) 思路 如果您不会自适应辛普森法,请戳这里学习 其实我们发现,如果我们设 \(f(x)\) 表示 \(x= ...

  7. SpringBoot-使用异步

    SpringBoot提供了异步的支持,上手使用十分的简单,只需要开启一些注解支持,配置一些配置文件即可! 编写方法,假装正在处理数据,使用线程设置一些延时,模拟同步等待的情况: service: @S ...

  8. 【MySQL】MySQL(三)存储过程和函数、触发器、事务

    MySQL存储过程和函数 存储过程和函数的概念 存储过程和函数是 事先经过编译并存储在数据库中的一段 SQL 语句的集合 存储过程和函数的好处 存储过程和函数可以重复使用,减轻开发人员的工作量.类似于 ...

  9. FastAPI 学习之路(四十六)WebSockets(二)

    上一篇文章,我们分享了WebSockets一些入门的,我们这节课,在原来的基础上,对于讲解的进行一个演示.我们最后分享了依赖token等.首先我们对上次的代码进行调整. 我们之前分享FastAPI 学 ...

  10. CentOS 用户与群组

    目录 1.用户管理 1.1.切换用户 1.2.添加用户 1.3.删除用户 1.4.修改用户 2.群组管理 2.1.查看群组 2.2.添加群组 2.3.删除群组 2.4.修改群组 1.用户管理 Linu ...