数据结构(5.3_5)——二叉树的线索化

news2025/2/22 18:06:34

第一种寻找中序前驱方法

 中序线索化

本质上就是一次中序遍历,只不过需要在一边遍历一边处理结点线索化

代码:


//全局变量pre 指向当前访问结点的前驱
ThreadNode* pre = NULL;


struct ElemType {
    int value;
};

//线索二叉树结点
typedef struct ThreadNode {
    ElemType data;
    struct ThreadNode* lchild, * rchild;
    int ltag, rtag;//左、右线索标志
} ThreadNode, * ThreadTree;
//中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree T) {
    if (T != NULL) {
        InThread(T->lchild); // 递归遍历左子树
        visit(T); // 访问根节点
        InThread(T->rchild); // 递归遍历右子树
    }
}
void visit(ThreadNode* q) {
    if (q->lchild == NULL) { // 左子树为空,建立前驱线索
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL) { // 建立前驱结点的后继线索
        pre->rchild = q;
        pre->rtag = 1;
    }
    pre = q; // 更新前驱为当前结点
}
  //中序线索化二叉树T
  void CreatePreThread(ThreadTree T) {
      pre = NULL;//pre初始为空
      if (T != NULL) {//非空二叉树才能线索化
          InThread(T);//先序线索化二叉树
          if (pre->rchild == NULL)
              pre->rtag = 1;//处理遍历的最后一个结点
      }
  }

先序线索化

本质上就是一次先序遍历,只不过需要在一边遍历一边处理结点线索化

代码:

//全局变量pre 指向当前访问结点的前驱
ThreadNode* pre = NULL;

struct ElemType {
    int value;
};

//线索二叉树结点
typedef struct ThreadNode {
    ElemType data;
    struct ThreadNode* lchild, * rchild;
    int ltag, rtag;//左、右线索标志
} ThreadNode, * ThreadTree;
//先序遍历二叉树,一边遍历一边线索化
void PreThread(ThreadTree T) {
    if (T != NULL) {
        visit(T); // 访问根节点
        if(T->ltag==0)//lchild不是前驱线索
            PreThread(T->lchild); // 递归遍历左子树
        if (T->rtag==0)//rchild不是前驱线索
            PreThread(T->rchild); // 递归遍历右子树
    }
}
void visit(ThreadNode* q) {
    if (q->lchild == NULL) { // 左子树为空,建立前驱线索
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL) { // 建立前驱结点的后继线索
        pre->rchild = q;
        pre->rtag = 1;
    }
    pre = q; // 更新前驱为当前结点
}
//先序线索化二叉树T
    void CreatePreThread(ThreadTree T){
        pre = NULL;//pre初始为空
        if (T != NULL) {//非空二叉树才能线索化
            PreThread(T);//先序线索化二叉树
            if (pre->rchild == NULL)
                pre->rtag = 1;//处理遍历的最后一个结点
        }
}

注意:若在PreThread函数中不添加

 if(T->ltag==0)

可能会导致某一结点的pre指针陷入无限循环 

后序线索化

本质上就是一次后序遍历,只不过需要在一边遍历一边处理结点线索化

代码:


//全局变量pre 指向当前访问结点的前驱
ThreadNode* pre = NULL;

struct ElemType {
    int value;
};

//线索二叉树结点
typedef struct ThreadNode {
    ElemType data;
    struct ThreadNode* lchild, * rchild;
    int ltag, rtag;//左、右线索标志
} ThreadNode, * ThreadTree;
//后序遍历二叉树,一边遍历一边线索化
void PostThread(ThreadTree T) {
    if (T != NULL) {
        PostThread(T->lchild); // 递归遍历左子树
        PostThread(T->rchild); // 递归遍历右子树
        visit(T); // 访问根节点
    }
}
void visit(ThreadNode* q) {
    if (q->lchild == NULL) { // 左子树为空,建立前驱线索
        q->lchild = pre;
        q->ltag = 1;
    }
    if (pre != NULL && pre->rchild == NULL) { // 建立前驱结点的后继线索
        pre->rchild = q;
        pre->rtag = 1;
    }
    pre = q; // 更新前驱为当前结点
}
//后序线索化二叉树T
    void CreatePreThread(ThreadTree T){
        pre = NULL;//pre初始为空
        if (T != NULL) {//非空二叉树才能线索化
            PostThread(T);//先序线索化二叉树
            if (pre->rchild == NULL)
                pre->rtag = 1;//处理遍历的最后一个结点
        }
}

为什么中序线索化和后序线索化不会遇到怕死循环问题 :

中序线索化:

  1. 线索化时机:在中序线索化过程中,一个节点的左线索是在其左子树遍历完成之后设置的,而右线索是在其右子树遍历完成之后设置的。这意味着在设置线索时,子树的遍历已经完成,因此不会覆盖未遍历的子节点指针。

  2. 线索标志:中序线索化过程中,每个节点有两个线索标志 ltag 和 rtag。在遍历过程中,如果 ltag 为 0,则表示左孩子指针指向的是实际的左子节点,需要继续递归遍历;如果为 1,则表示左孩子指针是指向中序前驱的线索。同样,rtag 用于指示右孩子指针是否为线索。

  3. 遍历顺序:中序遍历的顺序是左-根-右。这意味着在访问任何一个节点之前,它的左子树已经完全遍历并线索化,而它的右子树尚未遍历。因此,设置左线索是安全的,因为它不会影响右子树的遍历。

后序线索化:

  1. 线索化时机:在后序线索化过程中,节点的左线索和右线索是在其后序遍历的相应位置设置的。与中序线索化类似,这意味着在设置线索时,子树的遍历已经完成。

  2. 线索标志:后序线索化同样使用线索标志来区分子节点指针和线索。在遍历过程中,如果 ltag 为 0,则表示左孩子指针指向实际的左子节点;如果为 1,则表示左孩子指针是指向后序前驱的线索。右线索的设置是在遍历右子树和根节点之后。

  3. 遍历顺序:后序遍历的顺序是左-右-根。在设置右线索之前,右子树已经被完全遍历和线索化,因此不会影响到后续的遍历过程。

总的来说,中序和后序线索化之所以不会遇到先序线索化中的死循环问题,是因为它们在设置线索时已经完成了子树的遍历,因此不会覆盖未遍历的子节点指针。而先序线索化则需要额外注意,因为线索化可能发生在遍历子树之前,如果不正确处理,就可能会覆盖子节点指针,导致死循环。

总结:

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

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

相关文章

姜夔,师法自然的不仕道人

姜夔(ku),字尧章,号白石道人,约生于南宋绍兴二十四年(公元1154年),卒于南宋嘉定十四年(公元1221年),享年67岁。他的艺术成就涵盖了诗词、散文、书…

JAVA (IO流) day7.25

ok了家人们今天继续学习io流,废话不多说,我们一起看看吧 一.File类 1.1 File类的概述 通过 File 类与 IO 流进行搭配使用就可以通过 Java 代码将数 据从内存写入到硬盘文件 或者从硬盘文件读取到内存 中。 File 类就是文件和目录路径名的抽象表示形式…

ElasticSearch(es)倒排索引

目录 一、ElasticSearch 二、倒排索引 1. 正向索引 2. 倒排索引 具体细节 1. 文档分析 2. 索引构建 3. 索引存储 4. 词条编码 5. 索引优化 6. 查询处理 示例 总结 3. 正向和倒排 三、总结 倒排索引的基本概念 为什么倒排索引快 一、ElasticSearch Elasticsear…

让开发者生活更轻松的 JavaScript 字符串方法

前端岗位内推来了 JavaScript 最初被引入作为一种简单的客户端脚本语言,但现在,它已经成为一种真正的 WORA(一次编写到处运行)语言,使开发者能够构建桌面、移动、电视、CLI 和嵌入式应用程序。JavaScript 的初学者友好…

HiveSQL题——炸裂+开窗

一、每个学科的成绩第一名是谁? 0 问题描述 基于学生成绩表输出每个科目的第一名是谁呢? 1 数据准备 with t1 as(selectzs as name,[{"Chinese":80},{"Math":70}],{"English"…

IOS-04 Swift 中数组、集合、字典、区间、元组和可选类型

在 Swift 编程语言中,数据结构和类型的合理运用对于高效编程至关重要。接下来,我们将深入探讨数组、集合、字典、区间、元组和可选类型的相关知识。 一、数组(Array) (一)元素定义 可以通过多种方式定义数…

关于 OSPF 序列号范围 0x80000001-0x7FFFFFFF 正本清源

注:机翻,未校对。 正本:RFC 2328 OSPF Version 2 中相关解释 April 1998 12.1.6. LS sequence number 12.1.6. 序列号 The sequence number field is a signed 32-bit integer. It is used to detect old and duplicate LSAs. The space …

【React】详解 React Hooks 使用规则

文章目录 一、Hooks 的基本原则1. 只在最顶层调用 Hooks2. 只在 React 函数组件和自定义 Hooks 中调用 Hooks 二、常见 Hooks 及其使用规则1. useState2. useEffect3. useContext4. useReducer5. useMemo6. useCallback 三、常见错误及其解决方案1. 在条件语句中调用 Hooks2. 在…

「C++系列」数组

文章目录 一、数组1. 声明数组2. 初始化数组3. 访问数组元素4. 遍历数组注意事项示例代码 二、多维数组1. 声明二维数组2. 初始化二维数组3. 访问二维数组元素4. 遍历二维数组注意事项示例代码 三、指向数组的指针1. 声明指向数组的指针2. 通过指针访问数组元素3. 指针和数组的…

文件上传漏洞(ctfshow web151-161)

Web151 F12修改源代码 exts后面png改为php 这样就可以上传php的文件了 Web152: 考点:后端不能单一校验 就是要传图片格式,抓个包传个png的图片 然后bp抓包修改php后缀解析 然后放包 Web153-web156 在php代码中可以使用“{}”代替“[]” …

Go语言实战:基于Go1.19的站点模板爬虫技术解析与应用

一、引言 1.1 爬虫技术的背景与意义 在互联网高速发展的时代,数据已经成为新的石油,而爬虫技术则是获取这种“石油”的重要工具。爬虫,又称网络蜘蛛、网络机器人,是一种自动化获取网络上信息的程序。它广泛应用于搜索引擎、数据分…

机械学习—零基础学习日志(高数14——函数极限概念)

零基础为了学人工智能,真的开始复习高数 后续的速度要加快了~!~~~!! 概念 如何理解 方法一:吴军老师——无穷小是一种动态概念 函数极限,更多表达的是一种动态趋势,而不是一种静态的数值。以…

linux脚本:自动检测的bash脚本,用于检查linux的系统性能

目录 一、要求 二、脚本介绍 1、脚本内容 2、解释 3、使用方法: (1)脚本文件 (2)赋予权限 (3)执行结果 三、相关命令介绍 1、top (1)定义 (2&…

Springboot 整合Elasticsearch

1 java操作ES方式 1.1 操作ES 9300端口(TCP) 但开发中不在9300进行操作 ES集群节点通信使用的也是9300端口如果通过9300操作ES,需要与ES建立长连接 可通过引入spring-data-elasticsearch:transport-api.jar不在9300操作原因:1.springboot版本不同&…

Hive多维分析函数——With cube、Grouping sets、With rollup

有些指标涉及【多维度】的聚合,大的汇总维度,小的明细维度,需要精细化的下钻。 grouping sets: 多维度组合,组合维度自定义;with cube: 多维度组合,程序自由组合,组合为…

学习react-Provider解决props需要层层传递问题

1.组件数据传递问题 数据传递:A(顶级组件)-》B组件(子组件)、C组件(孙子组件)…很多组件 这样得通过props层层传递到下面的组件 还有另一种解决方法,即通过全局对象来解决,使用Prov…

Android 10.0 framework默认沉浸式状态栏功能实现

1. 前言 在10.0的系统rom定制化开发中,在实现状态栏的某些定制化开发中,在某些产品需要实现沉浸式状态栏,就是需要app 能全屏显示同样也能显示状态栏,接下来就来分析下相关的功能实现 如图: 2.framework默认沉浸式状态栏功能实现的核心类 frameworks\base\core\java\andro…

SpringCloud+FastAPI 打造AI微服务

Nacos注册微服务 注册接口服务(java)和图像ocr服务(python) springcloud业务层 fastapi推理层 postman调用接口层,接口层再通过openfegin,调用注册在nacos上推理层的微服务 参考文章SpringCloudPython 混合微服务,如何打造AI分布式…

Python数值计算(13)

1. 数学知识 虽然在给定了N个点以后,通过这个点的最小幂多项式是确定的,但是表达方式可不止一种,例如前面提到的系数方式,根方式,还有插值的Lagrange形式等。这里介绍另外一种表达方式: 显然这个式子最高次…

p28 vs环境-C语言实用调试技巧

int main() { int i0; for(i0;i<100;i) { printf("%d",i); } } 1.Debug 和Release的介绍 Debug通常称为调试版本&#xff0c;它包含调试信息&#xff0c;并且不做任何优化&#xff0c;便于程序员调试程序。 Release称为发布版本&#x…