【数据结构】- 详解线索二叉树(C 语言实现)

news2025/1/7 22:08:30

目录

一、线索二叉树的基本概念

二、构造线索二叉树

三、遍历线索二叉树


 


一、线索二叉树的基本概念

遍历二叉树是以一定规则将二叉树中的结点排列成一个线性序列,得到二叉树中结点的先序序列、中序序列或后序序列。这实质上是对一个非线性结构进行线性化操作,使得每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个(直接)前序和(直接)后继

但是,当以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直接得到结点在任一序列中的前驱和后继信息,这种信息只有在遍历的动态过程中才能得到,为此引入线索二叉树(Threaded Binary Tree)来保存这些动态过程中得到的有关前驱和后继的信息(线索化)。

虽然可以在每个结点中增加两个指针域来存放在遍历时得到的有关前驱和后继信息,但这样做使得结构的存储密度大大降低。由于有 n 个结点的二叉链表中必定有 n + 1 个空链域,因此可以利用这些空链域来存放结点的前驱和后继信息

试做如下规定:

  • 若结点有左子树,则左指针指向其左孩子,否则将左指针指向遍历序列中它的前驱结点

  • 若结点有右子树,则右指针指向其右孩子,否则将右指针指向遍历序列中它的后继结点

为了避免混淆,还需改变结点结构,增加两个标志域 ltag 和 rtag。当 ltag 和 rtag 为 0 时,表示 left 和 right 指向结点的左右孩子;当 ltag 和 rtag 为 1 时,表示 left 和 right 指向结点的前驱和后继

typedef struct ThrdBiTNode
{
    DataType data;
    struct ThrdBiTNode* left, * right;
    unsigned char ltag, rtag;  // 标志左右指针的类型,0 即非线索指针,1 即线索指针
}ThrdBiTNode;


二、构造线索二叉树

由于线索二叉树构造的实质是将二叉链表中的空指针改为指向前驱或后继的线索,而前驱或后继的信息只有在遍历时才能得到,因此线索化的过程即在遍历的过程中修改空指针的过程,可用递归算法。对二叉树按照不同的遍历次序进行线索化,可以得到不同的线索二叉树,包括先序线索二叉树、中序线索二叉树和后序线索二叉树。

下面重点介绍中序线索化的算法

为了记下遍历过程中访问结点的先后关系,附设一个指针 prev 始终指向刚刚访问过的结点,而指针 cur 指向当前访问的结点,由此记录下遍历过程中访问结点的先后关系。

快速创建一棵二叉树

ThrdBiTNode* BuyThrdBiTNode(DataType x)
{
    ThrdBiTNode* node = (ThrdBiTNode*)malloc(sizeof(ThrdBiTNode));
    if (NULL == node)
    {
        perror("malloc failed!");
        return NULL;
    }
    node->data = x;
    node->left = node->right = NULL;
    node->ltag = node->rtag = 0;
    return node;
}
​
// 快速创建一棵二叉树
ThrdBiTNode* CreatedBiTree()
{
    ThrdBiTNode* node1 = BuyThrdBiTNode(1);
    ThrdBiTNode* node2 = BuyThrdBiTNode(2);
    ThrdBiTNode* node3 = BuyThrdBiTNode(3);
    ThrdBiTNode* node4 = BuyThrdBiTNode(4);
    ThrdBiTNode* node5 = BuyThrdBiTNode(5);
    ThrdBiTNode* node6 = BuyThrdBiTNode(6);
​
    node1->left = node2;
    node1->right = node3;
    node2->left = node4;
    node2->right = node5;
    node3->left = node6;
​
    return node1;
}

二叉树中序线索化

// 以结点 *cur 为根的子树中序线索化
void _InOrderThreading(ThrdBiTNode* cur, ThrdBiTNode** pprev)
{
    if (cur == NULL)
        return;
​
    _InOrderThreading(cur->left, pprev);  // 左子树递归线索化
​
    //【建立当前结点的前驱线索】
    // 如果当前结点的左指针为空,则将当前结点的左指针指向前驱结点
    if (cur->left == NULL)
    {
        cur->left = *pprev;
        cur->ltag = 1;
    }
    //【建立前驱结点的后继线索】
    // 如果前驱结点的右指针为空,则将前驱结点的右指针指向当前结点
    if (*pprev != NULL && (*pprev)->right == NULL)
    {
        (*pprev)->right = cur;
        (*pprev)->rtag = 1;
    }
    //【保持 prev 指向 cur 的前驱】
    *pprev = cur;
    
    _InOrderThreading(cur->right, pprev);  // 右子树递归线索化
}
​
// 二叉树中序线索化
void InOrderThreading(ThrdBiTNode* root)
{
    if (root == NULL)
        return;
​
    ThrdBiTNode* prev = NULL;
    _InOrderThreading(root, &prev);
​
    // 处理最后一个结点(最右结点)的右指针
    prev->right = NULL;
    prev->rtag = 1;
}

Test.c

#include "ThrdBiTree.h"
​
int main()
{
    ThrdBiTNode* root = CreatedBiTree();
    InOrderThreading(root);
    return 0;
}


三、遍历线索二叉树

由于有了结点的前驱和后继信息,线索二叉树的遍历和在指定次序下查找结点的前驱和后继算法都变得简单。因此,若需经常查找结点所在遍历线性序列中的前驱和后继,则采用线索链表作为存储结构。

下面分 3 种情况讨论在线索二叉树中如何查找结点的前驱和后继。

  1. 在中序线索二叉树中查找

    (1) 查找 cur 所指结点的前驱

    • 若 cur->ltag == 1,则结点的前驱为 cur->left 指向的结点;

    • 若 cur->ltag == 0,则说明 *cur 有左子树,结点的前驱是遍历左子树时最后访问的一个结点(即左子树中最右下的结点)

    对中序线索二叉树进行【逆向】中序遍历

    // 找到树中最后一个被中序遍历的结点(即最右下的结点)
    ThrdBiTNode* LastNode(ThrdBiTNode* p)
    {
        while (p->rtag == 0)
            p = p->right;
        return p;
    }
    ​
    // 在中序线索二叉树中找到 *cur 的前驱结点
    ThrdBiTNode* PrevNode(ThrdBiTNode* cur)
    {
        if (cur->ltag == 1)
            return cur->left;
        else
            return LastNode(cur->left);
    }
    ​
    // 对中序线索二叉树进行【逆向】中序遍历
    void RevInOrder(ThrdBiTNode* root)
    {
        if (root == NULL)
        {
            printf("\n");
            return;
        }
    ​
        ThrdBiTNode* cur = LastNode(root);
        while (cur != NULL)
        {
            printf("%d ", cur->data);
            cur = PrevNode(cur);
        }
        printf("\n");
    }

    (2) 查找 cur 所指结点的后继

    • 若 cur->rtag == 1,则结点的后继为 cur->right 指向的结点;

    • 若 cur->rtag == 0,则说明 *cur 有右子树,结点的后继是遍历右子树时第一个访问的结点(即右子树中最左下的结点)

    对中序线索二叉树进行中序遍历(利用线索实现非递归算法)

    // 找到树中第一个被中序遍历的结点(即最左下的结点)
    ThrdBiTNode* FirstNode(ThrdBiTNode* p)
    {
        while (p->ltag == 0)
            p = p->left;
        return p;
    }
    ​
    // 在中序线索二叉树中找到 *cur 的后继结点
    ThrdBiTNode* NextNode(ThrdBiTNode* cur)
    {
        if (cur->rtag == 1)
            return cur->right;
        else
            return FirstNode(cur->right);
    }
    ​
    // 对中序线索二叉树进行中序遍历 
    void InOrder(ThrdBiTNode* root)
    {
        if (root == NULL)
        {
            return;
            printf("\n");
        }
    ​
        ThrdBiTNode* cur = FirstNode(root);
        while (cur != NULL)
        {
            printf("%d ", cur->data);
            cur = NextNode(cur);
        }
        printf("\n");
    }
  2. 在先序线索二叉树中查找 cur 所指结点的后继

    • 若 cur->rtag == 1,则结点后继为 cur->right 指向的结点;

    • 若 cur->rtag == 0,则说明 *cur 有右子树。按先序遍历的规则可知,*cur 的后继必为其左子树的根(若存在)或右子树的根

  3. 在后序线索二叉树中查找 cur 所指结点的前驱

    • 若 cur->ltag == 1,则结点前驱为 cur->left 指向的结点;

    • 若 cur->ltag == 0,且 cur->rtag == 0,则说明 *cur 有右子树,此时结点后继为其右子树的根,即 cur->right 指向的结点;若 cur->rtag == 1,则说明 *cur 没有右子树,但有左子树,此时结点的后继为其左子树的根,即 cur->left 指向的结点

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

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

相关文章

【同一局域网下】两台电脑之间互ping

两台电脑互ping 首先需要连接同一网咯关闭需要ping的电脑的防火墙 关闭防火墙步骤(以win11系统为例): 设置 --> 隐私和安全性 --> Windows 安全中心 打开Windows安全中心 防火墙和网络保护 --> 选择正在使用的网络 关闭 ping其他…

Unity 轨道展示系统(DollyMotion)

DollyMotion 🍱功能展示🥙使用💡设置路径点💡触发点位切换💡动态更新路径点💡事件触发💡设置路径💡设置移动方案固定速度方向最近路径方向 💡设置移动速度曲线 传送门 &a…

厦门城市内涝积水预防方案

随着城市化进程的加速,城市内涝问题日益凸显,给人们的生命财产安全带来了严重威胁。为了解决这一问题,城市内涝积水监测系统的应用逐渐受到广泛关注。本文将探讨城市内涝积水监测系统的优点及作用,为保障城市生命线的安全提供有力…

无电机光电测径仪稳定性好

目前市面上的在线测径仪主要是有电机的激光扫描式测径仪与无电机的光电平行光测径仪。均能完成外径尺寸的高精度尺寸检测,本文来简单介绍一下无电机光电测径仪的优势。 光电测径仪检测原理 发射镜头内置一个点光源,点光源发出的光通过透镜系统&#xf…

YashanDB入选2023年世界互联网大会领先科技奖成果集《科技之魅》

近日,由深圳计算科学研究院自主研发的“崖山数据库系统YashanDB”入编2023年世界互联网大会领先科技奖成果集《科技之魅》。此次入选,充分彰显了YashanDB在数据库技术领域的突破性创新成果。 《科技之魅》是世界互联网大会领先科技奖的重要成果&#xff…

智慧环保:视频监控平台EasyCVR与AI智能分析在环保领域的应用

人工智能(AI)视频分析技术在环保领域有着广泛的应用,通过智能识别和跟踪技术,AI视频分析可以实时监测空气质量、水质和噪音等环境指标,帮助环保部门及时发现污染源并进行有效治理,提高监测、管理和保护环境…

蓝桥杯第一天-----时间显示

文章目录 前言一、题目描述二、测试用例三、题目分析四、具体代码实现总结 前言 本章中将相信介绍蓝桥杯中关于时间显示的题目。 链接:https://www.lanqiao.cn/problems/1452/learning/ 一、题目描述 二、测试用例 三、题目分析 1.输入的时间为毫秒,毫…

结构体数组和结构体指针

在按键中断中&#xff0c;使用了结构体数组的语法&#xff1a; struct irq_dev imx6uirq; /* key设备 *///提取出GPIO对应的编号for (i 0; i < KEY_NUM; i) {imx6uirq.irqkeydesc[i].gpio of_get_named_gpio(imx6uirq.nd,"key-gpios", i);if (imx6uirq.irq…

十大排序算法及其特性最全总结,以408考察特性为基准

文章目录 一、冒泡排序&#xff08;Bubble Sort&#xff09;1.基本思想2.动图演示3.算法描述4.代码实现 二、快速排序&#xff08;Quick Sort&#xff09;☆1.基本思想2.动图演示3.算法描述4.代码实现 三、选择排序&#xff08;Selection Sort&#xff09;1.基本思想2.动图演示…

Linux创建与编辑视图

本博客将会详细讲解如何在Linux中如何编辑配置文件 输出重定向 对于一台设备而言&#xff0c;存在着两种设备&#xff0c;分别负责输入与输出&#xff1a; 显示器&#xff08;输出设备>&#xff09; 与 键盘&#xff08;输入设备<&#xff09; 对于Linux系统而言&#…

数据结构算法-分支定界算法

引言 应该记得这一张图片&#xff0c;在A星算法里面说过 那么现在说的是换一种方式实现 如何实现&#xff1f; 之前不撞南墙不回头的方法-深度优先搜索 的方式 广度优先搜索方式 广度优先搜索&#xff1a;就是说按照顺序入队 并且搜索扩展节点 探测四面八方&#xff0c;如此循环…

【c语言:常用字符串函数与内存函数的使用与实现】

文章目录 1. strlen函数1.1使用1.2模拟实现 2.strcmp函数2.1使用2.2模拟实现 3.strncmp函数3.1使用3.2模拟实现 4.strcpy函数4.1 使用4.2模拟实现 5.strcncpy5.1使用5.2模拟实现 6.strcat函数6.1使用6.2模拟实现 7.strncat函数7.1使用7.2模拟实现 8.strstr函数8.1使用8.2模拟实…

com.mongodb.MongoSocketOpenException: Exception opening socket

估计mongodb数据库没开启&#xff0c;或者链接错误了&#xff0c;谁又改了&#xff0c;唉 2023-11-29 16:19:45.818 INFO 39552 --- [127.0.0.1:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server 127.0.0.1:27017…

【JavaScript】3.3 JavaScript工具和库

文章目录 1. 包管理器2. 构建工具3. 测试框架4. JavaScript 库总结 在你的 JavaScript 开发之旅中&#xff0c;会遇到许多工具和库。这些工具和库可以帮助你更有效地编写和管理代码&#xff0c;提高工作效率。在本章节中&#xff0c;我们将探讨一些常见的 JavaScript 工具和库&…

001 - 安装Qt并配置环境

进入Qt中文网站的下载界面 &#x1f449;点此进入 点进去之后&#xff0c;你会看到如下界面&#xff1a; 这里下载的是Qt开源版的在线安装器&#xff0c; 如果你觉得下载速度很慢&#xff0c;可以挂个梯子。双击打开&#xff1a; 因为是在线安装&#xff0c;所以你需要输入电子…

4_最长公共前缀

我首先想到的方法就是暴力匹配法&#xff0c;刚开始我自己写的代码长这样&#xff0c;运行结果是错误的 。发现是循环的控制变量不对&#xff0c;导致计算结果出错。应该比较所有的vec[i][0]&#xff0c;vec[i][1]......&#xff0c;而不是比较vec[0][j]&#xff0c;vec[1][j].…

科研绘图配色

01 配色的基本原则 颜色需要有自身的意义。不同的颜色表示不同的分组&#xff0c;相近的颜色表示同一个分组&#xff1b;配色需要展现数据逻辑关系&#xff0c;突出关键数据&#xff0c;比如重要的数据用深色或暖色表示&#xff0c;不重要的数据用浅色或冷色表示。 色彩种类两…

通达OA inc/package/down.php接口未授权访问漏洞复现 [附POC]

文章目录 通达OA inc/package/down.php接口未授权访问漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 通达OA inc/package/down.php接口未授权访问漏洞复现 [附POC] 0x01 前言 免责声明&#x…

SimpleDateFormat在多线程下的安全问题

目录 情景重现 SimpleDateFormat解析 解决方案 局部变量 加锁 使用线程变量 使用DateTimeFormatter 情景重现 SimpleDateFormat类是Java开发中的一个日期时间的转化类。它可以满足绝大多数的开发场景&#xff0c;但是在高并发下会出现并发问题。接下来查看下文中的案例。…

导出CSV文件

从数据库导出csv文件 从HeidiSQL 导数据出来成.csv文件 SELECT * FROM csv INTO OUTFILE C:\\feiniu\\note\\csv\\demo.csv fields terminated by , CSV是什么 跟Excel表差不多 csv与excel对比&#xff1a; csv只能用于存储纯文本内容&#xff0c;excel不仅支持纯文本内容…