线索二叉树(c++)

news2024/11/15 6:50:06

1.引言:

二叉树的三种遍历方法能将二叉树中的结点按某种方式生成一个线性序列,能将一个非线性结构进行线性化操作。但随之也产生两个问题:

  • 遍历效率低
    在采用左右链表示方法作为二叉树的存储结构时,当二叉树更新后,并不能直接得到某个结点的直接前驱和直接后继,只能对二叉树进行重新遍历,
  • 浪费存储空间
    在一个具有n个结点的二叉树中,空指针域的数量为n+1,占用存储空间

 于是,我们希望空间最大化利用的同时,能够记录前驱后继结点。为此我们提出线索二叉树的概念。

2.线索二叉树

线索二叉树是指在二叉树上添加线索。通过线索二叉树,可以求任何一个结点的直接前驱和直接后继。 

现将某结点的空链域赋予特定含义,指向该结点的前驱后继,定义规则如下:

  • 若结点的左子树为空,则该结点的左孩子指针指向其前驱结点
  • 若结点的右子树为空,则该结点的右孩子指针指向其后继结点

这种指向直接前驱和直接后继的指针(链域)称为线索,将一棵普通的二叉树以某种次序遍历,并添加线索的过程称为线索化

下面以中根线索二叉树为例进行说明

中根线索二叉树是指在左右链所表示的二叉树中,将结点的空左链域指向二叉树中中根序列中该结点的直接前驱,将结点的空右链域指向二叉树中根序列中该结点的直接后继。 

注意:(a)图最后的 pre 和 t 都指向 C ;(b)中一开始header是D的线索前继,自身的线索后继,最后是C的右节点。

任意一个二叉树的左右链表示法都可以转化为线索二叉树,一个结点的左(右)链域可能是指向该结点的左(右)孩子,也可能指向该结点的直接前驱(后继)。为了区分结点的孩子链域是否为线索,于是为左右链域各添加一个标识:


(1) ltag:判别左链域lc是否为线索。当ltag=true 时,lc为指向当前结点直接前驱的线索,否则lc指向当前结点的左孩子结点;


(2) rtag:判别右链域rc是否为线索。当rtag=true 时,rc为指向当前结点后继的线索,否则rc指向当前结点的右孩子结点。

 转化过程分为如下两步:


        第一步为线索二叉树添加一个头结点header,根据前面的规定对头结点进行设置,然后从二叉树的根结点出发为二叉树添加线索,这一步在函数thTree_create 中实现。


        第二步为二叉树添加线案,方法是利用中根遍历方法遍历每一个结点,在遍历过程中需要记录当前结点t的上一个结点 pre,在第一步中设置pre 的初始值为header。如果t 的左孩子为线索,则t的直接前驱为pre;如果pre 的右孩子为线索,则pre 的直接后继为t。


        当第二步完成后,pre 应该为中根序列的最后一个结点,需要按要求设置其后继。

3.完整代码展示 

#include<iostream>
using namespace std;
typedef char datatype;

//左右链指针表示的线索二叉树的结点的定义
typedef struct thNode {
    datatype data;
    thNode* lc, * rc;
    bool ltag, rtag;
    thNode() :lc(NULL), rc(NULL), ltag(false), rtag(false) { //有线索为true
        data = 0;
    }
}*thTree;

//线索二叉树初始化
void createthTree(thTree& th) {
    char ch;
    cin >> ch;
    if (ch == '#') th = NULL;
    else {
        if (!(th = new thNode)) exit(-1);
        th->data = ch;
        createthTree(th->lc);
        createthTree(th->rc);
    }
}

//将一棵左右链表示的二叉树线索化
//将左右链指针表示的二叉树t线索化,pre为树t根结点的中根序的直接前驱
//pre和t 的位置一直在不断的进行变动;左孩子(false)和直接前驱(true)存一,右孩子和直接后继存一。
void convert_to_thTree(thTree t, thTree& pre) {
    if (t == NULL) return;

    convert_to_thTree(t->lc, pre); // 进入左子树

    //因为已经默认ltag,rtag为false,所以可以将以下内容简写
    /*
    if (t->lc != NULL) //t的左指针域为孩子结点
        t->ltag = false;
    else //t的左指针域为线索,指向其直接前驱pre
        t->lc = pre, t->ltag = true;

    if (pre->rc != NULL)  //pre的右指针域为孩子结点
        pre->rtag = false;
    else //pre的右指针域为线索,指向其直接后继t
        pre->rtag = true, pre->rc = t;
    */

    if (t->lc == NULL) {
        t->lc = pre, t->ltag = true;
    }

    if (pre->rc == NULL) {
        pre->rtag = true;
        pre->rc = t;
    }

    pre = t; //t的右子树中根序的第一个结点的直接前驱为t
    convert_to_thTree(t->rc, pre);
}

//将左右链表示的二叉树转化为带头结点header的线索二叉树,使之可以双向操作
void thTree_create(thTree& header, thTree t) {
    if (!(header = new thNode)) exit(-1);

    if (t == NULL) {
        header->lc = header;
        return;       //若根结点不存在,则该二叉树为空,让该头结点指向自身.
    }
    //header线索的直接后继设置为header
    header->rtag =true;
    header->rc = header;
    
    thTree pre;
    //原二叉树中根序的第一个结点的直接前驱为header
    header->lc = t;
    pre = header; 

    //开始递归输入线索化
    convert_to_thTree(t, pre);
    //此时结束了最后一个结点的线索化了,下面的代码把头结点的后继指向了最后一个结点.
    //并把最后一个结点的后继也指向头结点,此时树成为了一个类似双向链表的循环.
    pre->rc = header,pre->rtag =true; pre为中根序列的最后一个节点
    header->rc = pre; //该代码删除后,为单向
    return ;
}

//基于线索二叉树的中根遍历
//该遍历方法不需要递归或其他辅助容器,可以更快的获得二叉树的中根序列。   
void thTree_midOrder(thTree header) {
    thTree p = header->lc;     
    while (p != header) {
        while (p->ltag == false) p = p->lc;    //走向左子树的尽头
        cout<<p->data<<endl;
        //此时的p为中根序列的第一个结点,后面根据线索遍历
        while (p->rtag ==true && p->rc != header) {  //访问该结点的后续结点
            p = p->rc;
            cout << p -> data<<endl;
        }
        p = p->rc;
    }
    return ;
}

//注意:学会查找左子树的最左/最右孩子,右子树的最左或最右孩子

int main() {
    thTree B, T;
    createthTree(B);
    thTree_create(T, B);
    cout<<"中序遍历二叉树的结果为:"<<endl;
    thTree_midOrder(T);
    cout << endl;
}

//测试数据:ABD##EG###CF#H###

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

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

相关文章

【博客586】ipvs的hook点位置以及hook点钩子函数剖析

ipvs的hook点位置以及hook点钩子函数剖析 ipvs实现负载均衡的基础 ipvs其实是基于netfilter框架来挂载hook点&#xff0c;从而对流量进行dnat等操作 ipvs的hook点剖析 IPVS的实现利用了Netfilter的三个Hook点&#xff0c;分别是&#xff1a;NF_INET_LOCAL_IN、NF_INET_LOCAL_O…

【nvidia CUDA 高级编程】NVSHMEM 直方图——复制式方法

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

pandas时间序列,案列

一&#xff1a;pandas时间序列 1.1为什么要学习pandas中的时间序列 不管在什么行业&#xff0c;时间序列都是一种非常重要的数据形式&#xff0c;很多统计数据以及数据的规律也都和时间序列有着非常重要的联系&#xff0c;而且在pandas中处理时间序列是非常简单的 1.2生成一段时…

【Python技巧】:cmd查看Python版本号居然与自己电脑装的版本不一致,特此提出解决方案

项目场景&#xff1a; 大家好&#xff01;欢迎大家看我的博客&#xff0c;最近学习Python的GUI&#xff08;PyQt5&#xff09;的时候发现了自己电脑的一个python问题&#xff0c;我本想装一下PyQt5&#xff0c;顺手查了一下自己电脑的Python版本&#xff0c;没想到居然是Python…

哈希表(二)—— 开散列 / 拉链法 / 哈希桶的模拟实现

哈希表的基本思路是通过某种方式将某个值映射到对应的位置&#xff0c;这里的采取的方式是除留余数法&#xff0c;即将原本的值取模以后再存入到数组的对应下标&#xff0c;即便存入的值是一个字符串&#xff0c;也可以根据字符串哈希算法将字符串转换成对应的ASCII码值&#x…

Week 6 hw3-1 全连接网络反向传播推导

Week 6 hw3-1 全连接网络反向传播推导 折腾了半天&#xff0c;记录一下。 作业中网络由若干全连接层ReLU组成&#xff0c;输出层的函数为softmax&#xff0c;损失函数为交叉熵。 一、记号 设网络有nnn层。如图&#xff0c;当i<ni<ni<n时&#xff0c;我们有如下几条…

机器学习/人工智能 实验一:典型监督学习方法分类实践与比较分析

一、实验目的与要求 (1)利用所学习的监督学习方法完成目标识别实验方案的设计。 (2)编程并利用相关软件完成实验测试&#xff0c;得到实验结果。 (3)通过对实验数据的分析﹑整理&#xff0c;方法的对比&#xff0c;得出实验结论&#xff0c;培养学生创新思维和编写实验报告的能…

【PyTorch深度学习实践】09_卷积神经网络基础

文章目录1.卷积操作1.1 卷积操作1.2 padding-填充1.3 stride-步长1.4 pooling-池化1.5 基础版CNN代码示例1.6 完整CNN代码示例1.卷积操作 卷积神经网络概览 1.1 卷积操作 输入通道数卷积核通道数&#xff0c;卷积核个数输出通道数 1.2 padding-填充 padding是为了让源图像最…

FPGA图像处理HLS实现三种图像缩放算法,线性插值、双线性插值、双三次插值,提供HLS工程和vivado工程源码

目录一、三种图像缩放算法介绍线性插值双线性插值双三次插值二、HLS实现线性插值图像缩放三、HLS实现双线性插值图像缩放四、HLS实现双三次插值图像缩放五、HLS在线仿真并导出IP六、其他FPGA型号HLS在线仿真并导出IP七、zynq7100开发板vivado工程八、上板调试验证九、福利&…

纪念QT可直接安装的离线版最后版本5.14.2

为什么说纪念呢&#xff1f;因为&#xff0c;这个版本之后再也没有可下载下来安装的版本了&#xff0c;因为我们以后再也没有这么方便了。为是很么说纪念呢&#xff1f;因为我们从QT还很柔弱的时候开始就是使用的离线版。 以前用c#来做组态&#xff0c;自定义控件开发起来也还…

基础知识一览2

这里写目录标题1.XML2.1 XML中的转义字符2.2 CDATA区2.3 如何去约束XMl:DTD2.3.1 xml文件内部引用DTD约束2.3.2 xml文件引用外部DTD约束2.3.3 xml文件引用公共DTD约束1.XML xml的文件后缀名是.xmlxml有且只有一个根标签xml的标签是尖括号包裹关键字成对出现的&#xff0c;有开…

如何做好banner设计(banner设计要点包括哪些)

网页设计的Banner作为表达网站价值或者传达广告信息的视觉主体&#xff0c;一直在根据网络环境的变化而变化着&#xff0c;从表现形式到尺寸大小&#xff0c;再到创意的多元化&#xff0c;因此更需要我们网页设计师们对其设计创意进行丰富和完善&#xff0c;才能真正达到宣传的…

Elasticsearch入门——Elasticsearch7.8.0版本和Kibana7.8.0版本的下载、安装(win10环境)

目录一、Elasticsearch7.8.0版本下载、安装1.1、官网下载地址1.2、下载步骤1.3、安装步骤(需要jdk11及以上版本支持)1.4、启动后&#xff0c;控制台中文乱码问题解决二、Node下载、安装&#xff08;安装Kibana之前需要先安装Node&#xff09;2.1、Node官网下载地址2.2、Node下载…

Linux文字处理和文件编辑(三)

1、Linux里的配置文件&#xff1a; /etc/bashrc文件&#xff1a;该配置文件在root用户下&#xff0c;权限很高。~/.bashrc文件&#xff1a;只有当前用户登录时才会执行该配置文件。每次打开终端&#xff0c;都会自动执行配置文件里的代码。比如&#xff0c;alias md‘mkdir’就…

《2022年终总结》

2022年终总结 笔者成为社畜的一年&#xff0c;整整打了一年工&#xff01; 之前都说每年都有点变化&#xff0c;今年的变化可能就是更加懒散了&#xff0c;玩了更多的手机 就是运动的坚持更加多了&#xff0c;收入也增加了&#xff0c;哈哈&#xff01; 其实今年的变化不大&am…

41. 【农产品溯源项目前后端Demo】后端目录结构

本节介绍下后端代码的目录结构。 1. 实现用户管理、菜单管理、角色管理、代码自动生成等服务,归结为系统管理,是若依框架提供的能力。 2. ruoyi-traces实现农产品溯源应用的代码,如果要引入其他Java包,修改本模块的pom.xml文件。 1)config包加载配置文件数据,配置文件路…

FPGA:IIC验证镁光EEPROM仿真模型(纯Verilog)

目录日常唠嗑一、程序设计二、镁光模型仿真验证三、testbench文件四、完整工程下载日常唠嗑 IIC协议这里就不赘述了&#xff0c;网上很多&#xff0c;这里推荐两个&#xff0c;可以看看【接口时序】6、IIC总线的原理与Verilog实现 &#xff0c;还有IIC协议原理以及主机、从机Ve…

基于SpringBoot的车牌识别系统(附项目地址)

yx-image-recognition: 基于spring boot maven opencv 实现的图像深度学习Demo项目&#xff0c;包含车牌识别、人脸识别、证件识别等功能&#xff0c;贯穿样本处理、模型训练、图像处理、对象检测、对象识别等技术点 介绍 spring boot maven 实现的车牌识别及训练系统 基于…

3-1存储系统-存储器概述主存储器

文章目录一.存储器概述&#xff08;一&#xff09;存储器分类1.按在计算机中的作用&#xff08;层次&#xff09;分类2.按存储介质分类3.按存取方式分类4.按信息的可保存性分类&#xff08;二&#xff09;存储器的性能指标二.主存储器&#xff08;一&#xff09;基本组成1.译码…

6 个必知必会高效 Python 编程技巧

编写更好的Python 代码需要遵循Python 社区制定的最佳实践和指南。遵守这些标准可以使您的代码更具可读性、可维护性和效率。 本文将展示一些技巧&#xff0c;帮助您编写更好的 Python 代码 文章目录遵循 PEP 8 风格指南1.遵守 PEP 8 命名约定2. 使用描述性的和有意义的变量名…