《数据结构、算法与应用C++语言描述》-线索二叉树的定义与C++实现

news2024/11/17 19:36:19

_23Threaded BinaryTree

可编译运行代码见:GIithub::Data-Structures-Algorithms-and-Applications/_24Threaded_BinaryTree

线索二叉树定义

在普通二叉树中,有很多nullptr指针被浪费了,可以将其利用起来。

在这里插入图片描述

首先我们要来看看这空指针有多少个呢?对于一个有n个结点的二叉链表,每个结点有指向左右孩子的两个指针域,所以一共是2n个指针域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n-(n-1)=n+1个空指针域

对上图**(参考:《大话数据结构 溢彩加强版 程杰》160页图)**做中序遍历,得到了HDIBJEAFCG这样的字符序列,遍历过后,我们可以知道,结点I的前驱是D,后继是B,结点F的前驱是A,后继是C。也就是说,我们可以很清楚地知道任意一个结点,它的前驱和后继是哪一个结点。

可是这是建立在已经遍历过的基础之上的。在二叉链表上,我们只能知道每个结点指向其左右孩子结点的地址,而不知道某个结点的前驱是谁,后继是谁。要想知道,必须遍历一次。以后每次需要知道时,都必须先遍历一次。这样比较浪费时间。

我们可以考虑利用那些空地址,存放指向结点在某种遍历次序下的前驱和后继结点的地址。我们把这种指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree)。

我们把二叉树进行中序遍历后,将所有的空指针域中的rchild,改为指向它的后继结点。将所有空指针域中的lchild,改为指向当前结点的前驱。由此得出,经过线索化的二叉树变成了一个双向链表。双向链表相比于二叉树更容易找到某节点的前驱和后继节点。因此,如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择。

但是还有一个问题,如果将这些空指针作为线索后无法区分该指针是线索还是指向孩子节点,因此需要标志位LTag为True表示该节点的左指针是索引,RLag为true表示该节点的右指针是索引,反之不是索引。

代码

main.cpp

/*
Project name :			_24Threaded_BinaryTree
Last modified Date:		2023年11月28日17点06分
Last Version:			V1.0
Descriptions:			线索二叉树
*/
#include "inThreadedBinaryTreeChains.h"
int main() {
    inThreadedBinaryTreeChainsTest();
    return 0;
}

inThreadedBinaryTreeChains.h

/*
Project name :			_24Threaded_BinaryTree
Last modified Date:		2023年11月28日17点06分
Last Version:			V1.0
Descriptions:			线索二叉树链表表示
*/

#ifndef _24THREADED_BINARYTREE_INTHREADEDBINARYTREE_H
#define _24THREADED_BINARYTREE_INTHREADEDBINARYTREE_H
#include <iostream>
#include "binaryTree.h"
#include "inThreadedBinaryTreeNode.h"
using namespace std;
/*二叉树基础测试函数*/
void inThreadedBinaryTreeChainsTest();
template<class E>
class inThreadedBinaryTreeChains : public binaryTree<inThreadedBinaryTreeNode<E>>
{
public:
    /*二叉树的基础成员函数*/
    /*构造函数函数*/
    inThreadedBinaryTreeChains() {
        root = nullptr; treeSize = 0;
    }
    /*析构函数*/
    ~inThreadedBinaryTreeChains() { erase(); }
    /*当树为空时,返回true;否则,返回false*/
    bool empty() const { return treeSize == 0; }
    /*返回元素个数*/
    int size() const { return treeSize; }

    void inOrderThreaded()  // 中序遍历索引,就是中序遍历的时候添加索引
    {
        pre = nullptr;
        inOrderThreaded(root);
    }

    /*中序遍历二叉树,使用函数指针的目的是是的本函数可以实现多种目的*/
    void inOrder(void(*theVisit)(inThreadedBinaryTreeNode<E>*))
    {
        visit = theVisit;
        /*是因为递归,所以才要这样的*/
        inOrder(root);/*这里调用的是静态成员函数inOrder()*/
    }
    /*中序遍历---输出endl*/
    void inOrderOutput() { inOrder(output); cout << endl; }

    /*后续遍历二叉树,使用函数指针的目的是是的本函数可以实现多种目的*/
    void postOrder(void(*theVisit)(inThreadedBinaryTreeNode<E>*))
    {
        visit = theVisit;
        /*是因为递归,所以才要这样的*/
        postOrder(root);/*这里调用的是静态成员函数inOrder()*/
    }
    /*后序遍历---输出endl*/
    void postOrderOutput() { postOrder(output); cout << endl; }

    /*清空二叉树 这里必须使用后序遍历 不然会出错*/
    void erase()
    {
        postOrder(dispose);
        root = nullptr;
        treeSize = 0;
    }
    /*输入时为了将root根节点传递给createBiTree()函数*/
    void input(void)
    {
        createBiTree(root);
    }
private:
/*二叉树基础私有成员*/
    inThreadedBinaryTreeNode<E>* root;//指向根的指针
    int treeSize;//树的结点个数
    static inThreadedBinaryTreeNode<E>* pre;// 在线索化时使用的前驱temp
    static void (*visit)(inThreadedBinaryTreeNode<E>*);//是一个函数指针,返回值为void 函数参数为binaryTreeNode<E>*
    static void inOrder(inThreadedBinaryTreeNode<E>* t);
    static void inOrderThreaded(inThreadedBinaryTreeNode<E>* t);// 中序遍历索引,就是中序遍历的时候添加索引
    static void postOrder(inThreadedBinaryTreeNode<E>* t);
    static void dispose(inThreadedBinaryTreeNode<E>* t) { delete t; }
    static void output(inThreadedBinaryTreeNode<E>* t) { cout << t->element << " "; }
    /*创建二叉树---递归---作为私有成员只能被成员函数调用*/
    void createBiTree(inThreadedBinaryTreeNode<E>*& tree);
};
/*私有静态成员初始化*/
/*这里是静态函数指针成员的初始化,不初始化会引发LINK错误*/
template<class E>
void (*inThreadedBinaryTreeChains<E>::visit)(inThreadedBinaryTreeNode<E>*) = 0;      // visit function
// 这个地方需要做一个初始化,不做的话就会bug
template<class E>
inThreadedBinaryTreeNode<E>* inThreadedBinaryTreeChains<E>:: pre = nullptr;
/*中序遍历 递归*/
/*不受索引影响的中序遍历*/
template<class E>
void inThreadedBinaryTreeChains<E>::inOrder(inThreadedBinaryTreeNode<E>* t)
{
    if (t != nullptr)
    {
        // 在其左孩子不是索引时遍历
        if(!t->LTag)
            inOrder(t->leftChild);/*中序遍历左子树*/
        visit(t);/*访问树根*/
        // 在其右孩子不是索引时遍历
        if(!t->RTag)
            inOrder(t->rightChild);/*中序遍历右子树*/
    }
}
/*中序遍历索引 递归*/
/*本文写法可以保证在多次调用此函数下依然能正常执行,当插入新元素后再执行本函数可更新节点的索引*/
template<class E>
void inThreadedBinaryTreeChains<E>::inOrderThreaded(inThreadedBinaryTreeNode<E>* t)
{
    if (t != nullptr)
    {
        if(!t->LTag)
            inOrderThreaded(t->leftChild);/*中序遍历左子树*/
        if(!t->leftChild || t->LTag) // 没有左孩子,或者是第二次遍历即左孩子指向了他的前驱
        {
            t->LTag = true;
            t->leftChild = pre;
        }
        if(pre){
            if(!pre->rightChild || t->RTag)  // 如果前驱没有右孩子,或者是第二次遍历即右孩子指向了它的后继
            {
                pre->RTag = true;
                pre->rightChild = t;
            }
        }
        pre = t;
        if(!t->RTag)
            inOrderThreaded(t->rightChild);/*中序遍历右子树*/
    }
}
/*后序遍历 递归*/
/*不受索引影响的后序遍历*/
template<class E>
void inThreadedBinaryTreeChains<E>::postOrder(inThreadedBinaryTreeNode<E>* t)
{
    if (t != nullptr)
    {
        // 在其左孩子不是索引时遍历
        if(!t->LTag)
            postOrder(t->leftChild);/*后序遍历左子树*/
        // 在其右孩子不是索引时遍历
        if(!t->LTag)
            postOrder(t->rightChild);/*后序遍历右子树*/
        visit(t);/*访问树根*/
    }
}

/*创建二叉树---递归---模板的实现*/
template<class E>
void inThreadedBinaryTreeChains<E>::createBiTree(inThreadedBinaryTreeNode<E>*& tree)
{
    E data;
    cout << "Please enter the tree element:";
    while (!(cin >> data))
    {
        cin.clear();//清空标志位
        while (cin.get() != '\n')//删除无效的输入
            continue;
        cout << "Please enter the tree element:";
    }
    cin.get();
    /*针对char类型的特例*/
    if (typeid(data) == typeid(char)) {
        if (data == '#')
            tree = nullptr;
        else {
            treeSize++;
            tree = new inThreadedBinaryTreeNode<E>(data);
            createBiTree(tree->leftChild);
            createBiTree(tree->rightChild);
        }
    }
    else/*针对其他类型*/{
        if (data == 999)
            tree = nullptr;//当遇到999时,令树的根节点为nullptr,从而结束该分支的递归
        else
        {
            treeSize++;
            tree = new inThreadedBinaryTreeNode<E>(data);
            createBiTree(tree->leftChild);
            createBiTree(tree->rightChild);
        }
    }
}
#endif //_24THREADED_BINARYTREE_INTHREADEDBINARYTREE_H

inThreadedBinaryTreeChains.cpp

/*
Project name :			_24Threaded_BinaryTree
Last modified Date:		2023年11月28日17点06分
Last Version:			V1.0
Descriptions:			线索二叉树测试函数
*/
#include "inThreadedBinaryTreeChains.h"
void inThreadedBinaryTreeChainsTest(){
    cout << endl << "******************************inThreadedBinaryTreeChains()函数开始**********************************" << endl;
    cout << endl << "测试成员函数*******************************************" << endl;
    cout << "输入****************************" << endl;
    cout << "默认构造函数********************" << endl;
    inThreadedBinaryTreeChains<int> a;
    a.input();
    cout << "输出****************************" << endl;
    cout << "中序输出************************" << endl;
    //递归遍历
    a.inOrderThreaded();
    cout << "a.inOrderOutput() = ";
    a.inOrderOutput();
    cout << "后序输出************************" << endl;
    a.inOrderThreaded();
    cout << "a.postOrderOutput() = ";
    a.postOrderOutput();
    cout << "empty()*************************" << endl;
    cout << "a.empty() = " << a.empty() << endl;
    cout << "size()**************************" << endl;
    cout << "a.size() = " << a.size() << endl;
    cout << "erase()**************************" << endl;
    a.erase();
    cout << "a.inOrderOutput() = ";
    a.inOrderOutput();
    cout << "******************************inThreadedBinaryTreeChains()函数结束**********************************" << endl;
}

binaryTree.h

/*
Project name :			allAlgorithmsTest
Last modified Date:		2022年8月27日09点43分
Last Version:			V1.0
Descriptions:			二叉树的抽象类
*/

#ifndef _24THREADED_BINARYTREE_BINARYTREE_H
#define _24THREADED_BINARYTREE_BINARYTREE_H
template<class T>
class binaryTree
{
public:
    virtual ~binaryTree() {}
    virtual bool empty() const = 0;
    virtual int size() const = 0;
//    virtual void preOrder(void (*)(T*)) = 0;
    virtual void inOrder(void (*)(T*)) = 0;
    virtual void postOrder(void (*)(T*)) = 0;
//    virtual void levelOrder(void (*)(T*)) = 0;
};
#endif //_24THREADED_BINARYTREE_BINARYTREE_H

inThreadedBinaryTreeNode.h

/*
Project name :			_24Threaded_BinaryTree
Last modified Date:		2023年11月28日17点06分
Last Version:			V1.0
Descriptions:			线索二叉树节点结构体
*/

#ifndef _24THREADED_BINARYTREE_INTHREADEDBINARYTREENODE_H
#define _24THREADED_BINARYTREE_INTHREADEDBINARYTREENODE_H
template<class T>
struct inThreadedBinaryTreeNode
{
    T element;
    inThreadedBinaryTreeNode<T>* leftChild,//左子树
    *rightChild;//右子树
    bool LTag, RTag;// 左右子树指针是否为索引,为True则是索引,否则不是索引
    /*默认构造函数*/
    inThreadedBinaryTreeNode() { leftChild = rightChild = nullptr; LTag = RTag = false;}
    /*只初始化element*/
    inThreadedBinaryTreeNode(T melement)
    {
        element = melement;
        leftChild = rightChild = nullptr;
        LTag = RTag = false;
    }
    /*三个元素都初始化*/
    inThreadedBinaryTreeNode(T melement, inThreadedBinaryTreeNode<T>* mleftChild, inThreadedBinaryTreeNode<T>* mrightChild)
    {
        element = melement;
        leftChild = mleftChild;
        rightChild = mrightChild;
        LTag = RTag = false;
    }
};
#endif //_24THREADED_BINARYTREE_INTHREADEDBINARYTREENODE_H

测试

"C:\Users\15495\Documents\Jasmine\prj\_Algorithm\Data Structures, Algorithms and Applications in C++\_24Threaded BinaryTree\cmake-build-debug\_24Threaded_BinaryTree.exe"

******************************inThreadedBinaryTreeChains()函数开始**********************************

测试成员函数*******************************************
输入****************************
默认构造函数********************
Please enter the tree element:1
Please enter the tree element:2
Please enter the tree element:999
Please enter the tree element:999
Please enter the tree element:3
Please enter the tree element:999
Please enter the tree element:999
输出****************************
中序输出************************
a.inOrderOutput() = 2 1 3
后序输出************************
a.postOrderOutput() = 2 3 1
empty()*************************
a.empty() = 0
size()**************************
a.size() = 3
erase()**************************
a.inOrderOutput() =
******************************inThreadedBinaryTreeChains()函数结束**********************************

Process finished with exit code 0

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

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

相关文章

老师怎样预防校园欺凌的发生

作为老师&#xff0c;面对校园欺凌这个问题&#xff0c;我觉得有必要为各位老师提供一些实用的建议和策略。因为大家都知道&#xff0c;校园欺凌的存在不仅会对学生造成身心伤害&#xff0c;还会对整个教育环境产生负面影响。 关注学生的心理健康 校园欺凌往往与学生的心理问题…

手把手教你:基于python+Django的英文数据分析与可视化系统

系列文章 手把手教你&#xff1a;基于Django的新闻文本分类可视化系统&#xff08;文本分类由bert实现&#xff09;手把手教你&#xff1a;基于python的文本分类&#xff08;sklearn-决策树和随机森林实现&#xff09;手把手教你&#xff1a;基于TensorFlow的语音识别系统 目录…

Java Agent探针技术

前言 Java Agent基于字节码增强技术研发&#xff0c;支持自动埋点完成数据上报&#xff0c;Java Agent包含(并二次分发)opentelemetry-java-instrumentation CNCF的开源代码&#xff0c;遵循Apache License 2.0协议&#xff0c;在Java Agent包中对opentelemetry License进行了…

为什么国密SSL证书越来越受市场青睐

随着信息技术的迅猛发展&#xff0c;网络安全问题备受关注。在这个背景下&#xff0c;越来越多的单位纷纷选择国密SSL证书&#xff0c;以构建更为安全可靠的网络环境。那么&#xff0c;为什么这么多单位选择国密SSL证书呢&#xff1f; 1&#xff0c;国家政策支持 近年来&#…

战略制定|竞争战略管理分析六大常用工具

企业战略可从多个角度理解&#xff0c;体现为著名的5P模型。首先&#xff0c;从未来发展视角看&#xff0c;战略是一种计划(Plan)&#xff0c;指导企业朝向既定目标前进。而从过去的发展历程看&#xff0c;它呈现为一种模式(Pattern)&#xff0c;反映了企业的历史行为趋势。在产…

【Python】Selenium自动化测试框架

设计思路 本文整理归纳以往的工作中用到的东西&#xff0c;现汇总成基础测试框架提供分享。 框架采用python3 selenium3 PO yaml ddt unittest等技术编写成基础测试框架&#xff0c;能适应日常测试工作需要。 1、使用Page Object模式将页面定位和业务操作分开&#xff0…

uniapp+微信小程序监听返回事件

代码附在最后 适用场景&#xff1a;uniapp开发微信小程序 需求是我点击列表进入数据信息的详情界面&#xff0c;点击详情界面的收藏&#xff0c;返回上一界面后&#xff0c;更新列表中的收藏情况。 目录 一、使用onUnload监听页面卸载 二、使用getCurrentPages()获取当前页…

关于网站的favicon.ico图标的设置需要注意的几点

01-必须在网页的head标签中放上下面的说明&#xff1a; <link rel"shortcut icon" href"/favicon.ico">否则&#xff0c;浏览器虽然能读到图标&#xff0c;但是不会把图标显示在标签上。 02-浏览器会默认到网站根目录中读取文件favicon.ico&#x…

Java线程通信

线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…

MySQL数据库入门到大牛_基础_18_MySQL8其它新特性(MySQL基础部分最后一章;新特性概述;窗口函数;公用表表达式)

文章目录 1. MySQL8新特性1.1 MySQL8.0 新增特性1.2 MySQL8.0移除的旧特性 2. 新特性1&#xff1a;窗口函数2.1 使用窗口函数前后对比2.2 窗口函数分类2.3 语法结构2.4 分类讲解1. 序号函数2. 分布函数3. 前后函数4. 首尾函数5. 其他函数 2.5 小 结 3. 新特性2&#xff1a;公用…

vsphere系列 :虚拟机配置直通GPU后,启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案

vsphere中的虚拟机配置直通GPU后&#xff0c;启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案 vsphere中的虚拟机配置直通GPU后&#xff0c;启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案1、虚拟机配置GPU直通1、打开虚拟机选项2、点击编辑配置3、添加如…

SpringBoot+VUE3前后端分离-【支付宝支付】

1、支付宝沙箱应用申请 https://open.alipay.com/develop/sandbox/app 打开支付宝沙箱能够看到如下信息&#xff1a; 获取到appid&#xff1b; 2、获取应用私钥以及支付宝公钥 在接口加密方式选择公钥模式启用&#xff0c;根据操作即可获取应用公钥、应用私钥以及支付宝公钥…

别再让假的fiddler教程毒害你了,来看这套最全最新的fiddler全工具讲解

fiddler界面工具栏介绍 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; &#xff08;1&#xff09;WinConfig&#xff1a;windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig…

【蓝桥杯选拔赛真题26】C++字符串逆序 第十三届蓝桥杯青少年创意编程大赛C++编程选拔赛真题解析

目录 C/C++字符串逆序 一、题目要求 1、编程实现 2、输入输出 二、算法分析

Web框架与Django路由层

Web框架 一 web框架 Web框架&#xff08;Web framework&#xff09;是一种开发框架&#xff0c;用来支持动态网站、网络应用和网络服务的开发。这大多数的web框架提供了一套开发和部署网站的方式&#xff0c;也为web行为提供了一套通用的方法。web框架已经实现了很多功能&…

11-28渗透

用nmap扫描靶机1进行主机发现 已知靶机1的主机在172.16.17.0/24下 扫描结果如下 根据扫描结果看开启的服务怀疑172.16.17.177是靶机1 浏览器访问172.16.17.177页面得到如下 我们知道织梦cms系统默认管理路径是dede&#xff0c;登陆管理后台可以通过地址172.16.17.177/dede/i…

使用Sui天气预言机获取全球实时天气数据

新的Sui天气预言机为全球1000多个城市的建设者提供天气数据&#xff0c;并作为一个独特的随机数生成器&#xff0c;适用于需要可信赖的随机结果的游戏和投注应用。它由基于Sui的智能合约和一个从OpenWeather API获取天气数据的后端服务组成&#xff0c;任何人都可以将天气数据集…

OPENWRT解决配置pppoe后无法光猫路由管理界面

一、新建一个wan口 二、设置流量转发 设置完成后保存应用即可

【论文阅读笔记】Prompt-to-Prompt Image Editing with Cross-Attention Control

【论文阅读笔记】Prompt-to-Prompt Image Editing with Cross-Attention Control 个人理解思考基本信息摘要背景挑战方法结果 引言方法论结果讨论引用 个人理解 通过将caption的注意力图注入到目标caption注意力中影响去噪过程以一种直观和便于理解的形式通过修改交叉注意力的…