高级树结构之线索化二叉树

news2025/1/22 19:33:02

文章目录

  • 一 线索化二叉树简介
  • 二 线索化规则
  • 三 前序线索化
    • 3.1 代码演示
  • 四 中序线索化
    • 4.1 代码演示
  • 五 后序线索化
    • 5.1 代码演示

一 线索化二叉树简介

  • 线索化:将一颗二叉树的结点指向为空的指针,线索化为某一种顺序遍历的的指向下一个按顺序的结点的指针
  • 一颗为顺序化的二叉树的前序遍历顺序
    在这里插入图片描述
  • 对其前序遍历线索化后,遍历顺序依次为:
    在这里插入图片描述

二 线索化规则

  • 结点的左指针,指向当前遍历顺序的直接前驱结点。
  • 结点的右指针,指向当前遍历顺序的直接后继结点。
  • 上图前序遍历线索化之后的指向如下图:
    在这里插入图片描述
  • 为了区分某个结点的指针指向是其左右孩子?还是某种顺序遍历线索【直接前驱、直接后继结点】?
  • 需要在结点的结构体中增加一组标志位【左、右标志位】,来区分左右指针的指向代表的意义
    • leftTag / rightTag为1,表示左 / 右指针指向的是前驱 / 后继结点
    • leftTag / rightTag为0,表示左 / 右指针指向的是左孩子 / 右孩子
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;

三 前序线索化

  • 二叉树结构
    在这里插入图片描述
    在这里插入图片描述
  • 具体的实现代码,希望诸君认真阅读体会!

3.1 代码演示

#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点

Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}

//前序遍历线索化
void preOrderThreaded(Node root) {
    if (root == NULL) return;

    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //----------------------

    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        preOrderThreaded(root->left);
    }
    if (root->rightTag == 0) {
        preOrderThreaded(root->right);
    }
}

//前序遍历
void preOrder(Node root) {
    while (root) {
        cout << root->element << " ";
        if (root->leftTag == 0)
            root = root->left;
        else
            root = root->right;
        /*这里无所谓右边是孩子,还是线索*/
    }
}
int main() {
    Node a = createNode('A');
    Node b = createNode('B');
    Node c = createNode('C');
    Node d = createNode('D');
    Node e = createNode('E');
    a->left = b;
    b->left = d;
    a->right = c;
    b->right = e;
    preOrderThreaded(a);
    cout << "先序遍历的结果为:";
    preOrder(a);
    cout << endl;
    return 0;
}

在这里插入图片描述

四 中序线索化

  • 二叉树中序线索化的结构
    在这里插入图片描述
    在这里插入图片描述
  • 中序遍历线索化相较于前序遍历线索化,只具体的线索化代码的位置即可。
  • 对二叉树进行中序便利的代码实现稍有复杂。

4.1 代码演示

  • 具体的实现代码,希望诸君认真阅读体会!
#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点

Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}

//中序遍历线索化
void inOrderThreaded(Node root) {
    if (root == NULL) return;
    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        inOrderThreaded(root->left);
    }

    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //-----------------------

    if (root->rightTag == 0) {
        inOrderThreaded(root->right);
    }
}

void inOrder(Node root) {
    while (root) {
        //先走到最左边结点
        //如果左边一直都不是线索,那就一直往左找,直到找到一个左边是线索为止
        while (root && root->leftTag == 0) {
            root = root->left;
        }
        //中序开始打印
        cout << root->element << " ";
        //左边打印完就该打印,右边 右边如果是线索化之后的结果表示 是下一个节点
        while (root && root->rightTag == 1) {
            root = root->right;
            cout << root->element << " ";
        }
        //最后继续向右节点开始 重复上述操作。
        root = root->right;
    }
}

int main() {
    Node a1 = createNode('A');
    Node b1 = createNode('B');
    Node c1 = createNode('C');
    Node d1 = createNode('D');
    Node e1 = createNode('E');
    a1->left = b1;
    b1->left = d1;
    a1->right = c1;
    b1->right = e1;
    pre = NULL;
    inOrderThreaded(a1);
    cout << "中序遍历的结果为:";
    inOrder(a1);
    cout << endl;
    return 0;
}

在这里插入图片描述

五 后序线索化

  • 二叉树后序遍历线索化的结构:
    在这里插入图片描述
    在这里插入图片描述
  • 后序遍历,需要先完成左右子树的遍历。左边子树遍历完成后,并不一定能找到右子树的根结点。比如:在遍历完左子数根节点B之后,就无法找到右树的根结点C。所以需要在节点结构体中设置设置指向双亲节点的指针。

5.1 代码演示

  • 具体的实现代码,希望诸君认真阅读体会!
#include<iostream>
using namespace std;
typedef char E;
typedef struct TreeNode {
    E element;
    struct TreeNode *left;
    struct TreeNode *right;
    //左、右标志位
    // 为1表示:这一边指向的是线索;为0表示:正常的孩子结点
    int leftTag, rightTag;
    //为后序遍历准备,指向双亲结点
    struct TreeNode *parent;
} *Node;
Node pre = NULL;//保存前一个结点

Node createNode(E element) {
    Node node = (Node) malloc(sizeof(struct TreeNode));
    node->left = node->right = NULL;
    node->leftTag = node->rightTag = 0;
    node->element = element;
    return node;
}

//后序遍历线索化
void postOrderThreaded(Node root) {
    if (root == NULL) return;

    //注意只有标志为0,才可以继续进行,否则,为线索
    if (root->leftTag == 0) {
        postOrderThreaded(root->left);
        //左边完事之后,如果不为空,那么设定父子关系
        if (root->left != NULL)
            root->left->parent = root;
    }
    if (root->rightTag == 0) {
        postOrderThreaded(root->right);
        //右边完事之后,如果不为空,那么设定父子关系
        if (root->right != NULL)
            root->right->parent = root;
    }

    //---------线索化------------
    //判断当前结点的左边是否为空,如果是,就指向上一个结点
    if (root->left == NULL) {
        root->left = pre;
        //修改标志位
        root->leftTag = 1;
    }
    //判断上一个结点的右边是否为NULL
    // 如果为空,就进行线索化,指向当前结点
    if (pre && pre->right == NULL) {
        pre->right = root;
        pre->rightTag = 1;
    }
    pre = root;
    //---------------------------
}

void postOrder(Node root) {
    //记录上一次遍历的结点
    Node last = NULL, node = root;
    while (node) {
        //依旧是从整棵树最左边开始 同时加入防止无线循环的条件
        while (node->left != last && node->leftTag == 0) {
            node = node->left;
        }
        //左边遍历完了,如果右边是线索,就一路向前
        while (node && node->rightTag == 1) {
            cout << node->element << " ";
            last = node;
            node = node->right;
        }
        if (node == root && node->right == last) {
            //当左子树便利完成,我们需要去寻找左子树根节点的兄弟节点
            //通过parent拿到兄弟节点,但是如果当前结点是根节点,需要特殊处理,因为根节点没有父节点
            cout << node->element << " ";
            //后序遍历中,根节点一定是最后一个节点。所以直接返回就可以了。
            return;
        }
        //如果当前节点的上一个遍历的结点,那么就继续进行
        while (node && node->right == last) {
            cout << node->element << " ";
            last = node;
            node = node->parent;
        }
        //从左子树遍历上来,当前节点的右边,要么是线索,要么是右子树。
        if (node && node->rightTag == 0) {
            //如果不是线索,就直接走右边,如果是等到下一轮再说
            node = node->right;
        }
    }
}

int main() {
   	Node a2 = createNode('A');
    Node b2 = createNode('B');
    Node c2 = createNode('C');
    Node d2 = createNode('D');
    Node e2 = createNode('E');
    a2->left = b2;
    b2->left = d2;
    a2->right = c2;
    b2->right = e2;
    pre = NULL;
    postOrderThreaded(a2);
    cout << "后序遍历的结果为:";
    postOrder(a2);
    return 0;
}

在这里插入图片描述

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

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

相关文章

虚拟主机3种方式nginx/apache+跨域知识点整理

目录referer、prototype、array、json笔记整理: [http://t.csdn.cn/s4P8x](http://t.csdn.cn/s4P8x)虚拟主机3种方式nginx/apache跨域知识点整理一、Apache基于多IP、多端口、多域名访问1、添加网卡三种方法1、虚拟机添加网络适配器2、命令添加3、用nmtui 添加ip地址2、添加配置…

Jwt 最流行的跨域身份验证解决方案

1. 什么是JWT JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JWT被设计为紧凑且安全的&#xff0c;特别适用于分…

Python制做一个电脑通知小工具,再也不怕忘记事情拉~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ Windows不是有个消息通知功能&#xff0c;挺喜欢这个功能的&#xff0c; 但是不太方便使用&#xff0c;也懒得去研究&#xff0c; 于是准备用Python自己写一个&#xff0c;通过设定通知的间隔时…

Pikachu靶场暴力破解通关

目录 字典获取 BP四种攻击模式 一、Sniper(狙击手模式) 二、Battering ram(攻城锤模式) 三、Pitchfork(叉子模式) 四、Cluster bomb(炸弹模式) 靶场练习 基于表单的暴力破解 验证码绕过(on server) 验证码绕过(on client) token防爆破? 字典获取 在github里找到心…

公司注册商标的流程是什么

公司商标注册流程是什么? 1、商标查询。查询有关商标登记注册情况&#xff0c;以了解自己准备申请注册的商标是否与他人已经注册或正在注册的商标相同或近似的程序; 2、申请文件准备。商标注册申请书;)商标图样;申请注册集体商标、证明商标的&#xff0c;应当提交申请人主体资…

发布了一个jar包到中央仓库,我的心好累…

原创&#xff1a;微信公众号 码农参上&#xff0c;欢迎分享&#xff0c;转载请保留出处。 哈喽大家好啊&#xff0c;我是Hydra。 前几天我在网上冲浪的时候&#xff0c;看见有一个老铁在git上给我提了一个issue&#xff1a; 万万没想到&#xff0c;有一天我写的烂代码居然也会…

家庭装修流程五大步骤是怎样的,家庭装修有什么注意事项!

家庭装修流程五大步骤是怎样的家庭装修有什么注意事项&#xff01;现在的人们不是在城里面购买了楼房&#xff0c;就是在家里面进行翻修房子&#xff0c;不管是哪一种&#xff0c;都肯定是需要用到装修的。但是有很多的人们对于装修方面并不是很了解。想知道家庭装修流程五大步…

Qt之对话框(QDialog)

文章目录一、对话框的概念二、与QWidget的区别三、对话框2种显示方法四、对话框返回值的概念本节示例提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、对话框的概念 对话框是和用户简短交互的一种窗口。如&#xff1a;登录界面&#xff0c;关于界面…

性能-可观测数据、量化指标、性能问题、压测目标

可观测的数据数据指标 服务应用&#xff1a;CPU利用率、内存、ygc次数接口&#xff1a;核心关注接口的请求响应时间&#xff08;平均响应时间、P90、P95&#xff0c;核心关注后面2个&#xff09;、慢请求情况数据库&#xff1a;主要关注CPU使用率、内存使用率Redis&#xff1a…

前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-01

本文来研究写webpack-theme-color-replacer webpack 的实现逻辑和原理。 上一篇我们讲过&#xff0c; webpack-theme-color-replacer webpack 基本思路就是&#xff0c;webpack构建时&#xff0c;在emit事件&#xff08;准备写入dist结果文件时&#xff09;中&#xff0c;将即将…

Linux ALSA 之五 ALSA Proc Info

ALSA Proc Info一、概述二、Proc Files of Alsa Driver1、/proc/asound/xxx 简述2、创建 /proc/asound 目录树2.1 /proc/asound/version 文件2.2 /proc/asound/devices 文件2.3 /proc/asound/cards 文件2.4 /proc/asound/cardx 目录2.5 /proc/asound/pcm 文件一、概述 Linux系…

以“辛”为鉴,直播电商如何“知兴替”?

直播电商行业近年来创造了一个又一个销售神话。辛选2022年双11期间&#xff0c;18场直播销售额破亿。罗永浩在淘宝的首秀&#xff0c;6小时带货2亿元。直播间内外似乎一片繁华&#xff0c;但与此同时&#xff0c;也有许多曾经创造了带货奇迹的主播被市场淘汰&#xff0c;淡出视…

【笔记:第2课】学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春

文章目录前言来源正文小结前言 创作开始时间&#xff1a;2023年1月9日20:11:06 如题&#xff0c;学习一下RISC-V。 来源 https://www.bilibili.com/video/BV1Q5411w7z5?p2&vd_source73a25632b4f745be6bbcfe3c82bb7ec0 正文 计算机硬件组成&#xff1a; 总线CPU&…

迷宫问题 | 深度优先

目录 一、说明 二、步骤 三、代码 四、结果 一、说明 什么是深度优先&#xff1f; DFS即Depth First Search&#xff0c;深度优先搜索属于图算法的一种&#xff0c;是一个针对图和树的遍历算法&#xff0c;利用深度优先搜索算法可以产生目标图的相应拓扑排序表&#xff0c…

身兼数据科学家和自由职业者,算算我在2022赚了多少钱?

2022年,我作为自由职业者数据科学家赚了多少钱&#xff1f;长按关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩大家好&#xff0c;首先&#xff0c;我已经等了很久了。2…

保姆级 | 最新Burpsuite安装配置

文章目录 0x00 前言 0x01 环境说明 0x02 准备工作 0x03 安装JDK 0x04 配置JDK环境 0x05 Burpsuite安装 0x06 Burpsuite环境配置 0x07 Burpsuite设置代理 0x08 Burpsuite使用验证 0x09 总结 0x00 前言 Burp Suite 是用于攻击 web 应用程序的集成平台&#xff0c;包含了…

mongodb 中做 join 的方法

【问题】Imagine you have a collection for posts, and each of these posts has the attribute userid: ObjectId( ), where ObjectID is referencing a document in the Users collection.How would you go about retrieving the user information (in this case, the user …

GC耗时高,原因竟是服务流量小?

简介 最近&#xff0c;我们系统配置了GC耗时的监控&#xff0c;但配置上之后&#xff0c;系统会偶尔出现GC耗时大于1s的报警&#xff0c;排查花了一些力气&#xff0c;故在这里分享下。 发现问题 我们系统分多个环境部署&#xff0c;出现GC长耗时的是俄罗斯环境&#xff0c;…

高校舆情监控系统建设(TOOM)如何做好教育行业舆情监控方案?

高校作为高密度学生聚集地&#xff0c;舆情管理上&#xff0c;需要保持高度的警惕性。高校中大学生是活跃在互联网上的重要群体&#xff0c;他们作为文化水平较高、思维较活跃的特殊群体&#xff0c;其网络中的言论合集往往会引发社会关注。高校舆情监控系统建设(TOOM)如何做好…

Sapped of vitality 生机已被耗尽 | 经济学人社论高质量双语精翻

选自TE20221217&#xff0c;leaders The global economy&#xff1a;Sapped of vitality 世界经济&#xff1a;生机已被耗尽 Why are the rich world’s politicians giving up on economic growth? 为什么发达国家的政客们不再追求经济增长目标&#xff1f; The prospect of …