总目录:https://blog.csdn.net/treesorshining/article/details/125726400

文章目录

  • 1.概述
  • 2.基本操作
    • 2.1 结构体构造
    • 2.2 初始化
    • 2.3 左插入结点
    • 2.4 右插入结点
    • 2.5 访问结点并建立线索
    • 2.6 中序遍历二叉树并建立线索
    • 2.7 中序线索化二叉树
    • 2.8 找到第一个被中序遍历的结点
    • 2.9 在中序线索二叉树中找到某一结点的后继结点
    • 2.10 利用中序线索树完成非递归中序遍历
  • 3.完整代码
  • 4.运行结果

1.概述

在含有n个结点的二叉树中,有n+1个空指针,即每个度为1的结点有一个空指针,每个叶子结点有两个空指针,这就造成了空间的浪费。可以通过一些方法将这些空指针充分利用起来,即将这些空指针改成指向前驱或后继的线索,可以视为在原本的二叉树上用空指针建立一个线索链表,加上了线索的二叉树也可称为线索二叉树。

2.基本操作

2.1 结构体构造

// 线索二叉树结点
typedef struct ThreadNode {char data;  // 数据域// 左孩子、右孩子struct ThreadNode *lchild, *rchild;// 左、右线索标志// tag==0表示指针指向孩子// tag==1表示指针指向线索int ltag, rtag;
} ThreadNode, *ThreadTree;// 全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;

2.2 初始化

建立一个根节点,同时将左右孩子指针置空,将左右tag设为0。

// 初始化
void InitTree(ThreadNode* &T) {// 初始化可将根节点置空,空树T = NULL;// 插入根节点// 分配空间T = (ThreadNode*)malloc(sizeof(ThreadNode));// 赋值T->data = 'A';// 初始tag设为0T->ltag = 0;T->rtag = 0;// 初始无左右子树T->lchild = NULL;T->rchild = NULL;
}

2.3 左插入结点

与一般二叉树插入类似,不过要注意修改tag的值。

// 左插入结点
bool InsertLeftTreeNode(ThreadNode* &T, char x) {// 分配空间ThreadNode *p = (ThreadNode*)malloc(sizeof(ThreadNode));// 分配空间失败的情况if(p == NULL) {return false;}// 数据域赋值p->data = x;// 将tag值设为0p->ltag = 0;p->rtag = 0;// 初始插入,无左右孩子,置空p->lchild = NULL;p->rchild = NULL;// 作为指定插入结点的左孩子T->lchild = p;return true;
}

2.4 右插入结点

与一般二叉树插入类似,不过要注意修改tag的值。

// 右插入结点
bool InsertRightTreeNode(ThreadNode* &T, char x) {// 分配空间ThreadNode *p = (ThreadNode*)malloc(sizeof(ThreadNode));// 分配空间失败的情况if(p == NULL) {return false;}// 数据域赋值p->data = x;// 将tag值设为0p->ltag = 0;p->rtag = 0;// 初始插入,无左右孩子,置空p->lchild = NULL;p->rchild = NULL;// 作为指定插入结点的左孩子T->rchild = p;return true;
}

2.5 访问结点并建立线索

遍历中访问结点,在遍历的同时建立前驱及后继线索。

// 用于访问结点
void visit(ThreadNode *q) {// 左子树为空,建立前驱线索if(q->lchild == NULL) {// 令其左指针指向前驱q->lchild = pre;// 将tag值修改为1,表示此处左指针指向的是前驱,即左孩子指针是线索q->ltag = 1;if(pre != NULL) {printf("建立前驱线索:%c-->%c\n", q->data, pre->data);} else {printf("建立前驱线索:%c-->%s\n", q->data, "NULL");}}// 设立后继线索时前驱不能为空(无前驱无法建立后继)// 右孩子指针为空,表示可以建立后继线索if(pre != NULL && pre->rchild == NULL) {// 建立前驱结点的后继线索pre->rchild = q;// 将tag值修改为1,表示此处右指针指向的是后继,即右孩子指针是线索pre->rtag = 1;printf("建立后继线索:%c-->%c\n", pre->data, q->data);}// 线索建立完成后,前驱指针移动,指向当前指向结点处pre = q;
}

2.6 中序遍历二叉树并建立线索

实际上就是进行中序遍历,一边遍历一边线索化,因为想得到一个结点的前驱或者后继,只能在遍历时取得。

// 中序线索化二叉树
// 实际上就是进行中序遍历,一边遍历一边线索化
// 因为想得到一个结点的前驱或者后继,只能在遍历时取得
// 线索化在visit函数中完成
void InThread(ThreadTree T) {if(T != NULL) {InThread(T->lchild);visit(T);InThread(T->rchild);}
}

2.7 中序线索化二叉树

// 中序线索化二叉树
void CreateInThread(ThreadTree T) {// pre初始为NULLpre = NULL;// 若二叉树为空,则不可线索化if(T != NULL) {// 中序线索化二叉树InThread(T);// 对于最后一个结点的处理// 由于访问完成后,pre和p都指向最后一个结点// 按照线索化的要求,如果最后一个结点右孩子指向NULL,则可视为其有后继结点// 应将tag设为1if(pre->rchild == NULL) {pre->rtag = 1;printf("建立后继线索:%c-->%s\n", pre->data, "NULL");}}
}

2.8 找到第一个被中序遍历的结点

找到以p为根的子树中,第一个被中序遍历的结点。

// 找到以p为根的子树中,第一个被中序遍历的结点
// 即后继结点
ThreadNode *FirstNode(ThreadNode *p) {// 循环找到最左下结点(不一定是叶子结点)// 当ltag==0时,说明其线索二叉树此结点不存在左线索,那么一定有左子树while(p->ltag == 0) {p = p->lchild;}return p;
}

2.9 在中序线索二叉树中找到某一结点的后继结点

要找到一个结点的后继,如果右孩子指针tag==1,直接用线索即可,如若不为1,那么就要根据中序遍历的特点来寻找后继结点,即左根右。如果该结点有右孩子且右孩子为叶子结点,那么这就是其后继结点,如果该节点有右孩子,且为分支结点,那么后继结点就应该是该节点右子树的最左下结点。

// 在中序线索二叉树中找到结点p的后继结点
// 要找到一个结点的后继,如果右孩子指针tag==1,直接用线索即可
// 如若不为1,那么就要根据中序遍历的特点来寻找后继结点,即左根右
// 如果该结点有右孩子且右孩子为叶子结点,那么这就是其后继结点
// 如果该节点有右孩子,且为分支结点,那么后继结点就应该是该节点右子树的最左下结点
// 注意:若没有线索,则一定存在右子树(根据线索二叉树特点)
ThreadNode *NextNode(ThreadNode *p) {// 如果右孩子结点无线索if(p->rtag == 0) return FirstNode(p->rchild);// 如果右孩子结点有线索则直接利用线索找到结点即可else return p->rchild;
}

2.10 利用中序线索树完成非递归中序遍历

// 对中序线索二叉树进行中序遍历(利用线索非递归实现)
// 首先找到根节点最左下结点,访问之后,通过NextNode函数找后继结点访问即可
void InOrder(ThreadNode *T) {for(ThreadNode *p = FirstNode(T);p != NULL;p = NextNode(p)) {visit1(p);}
}

3.完整代码

#include<stdio.h>
#include<stdlib.h>// 线索二叉树结点
typedef struct ThreadNode {char data;  // 数据域// 左孩子、右孩子struct ThreadNode *lchild, *rchild;// 左、右线索标志// tag==0表示指针指向孩子// tag==1表示指针指向线索int ltag, rtag;
} ThreadNode, *ThreadTree;// 全局变量pre,指向当前访问结点的前驱
ThreadNode *pre = NULL;// 初始化
void InitTree(ThreadNode* &T) {// 初始化可将根节点置空,空树T = NULL;// 插入根节点// 分配空间T = (ThreadNode*)malloc(sizeof(ThreadNode));// 赋值T->data = 'A';// 初始tag设为0T->ltag = 0;T->rtag = 0;// 初始无左右子树T->lchild = NULL;T->rchild = NULL;
}// 左插入结点
bool InsertLeftTreeNode(ThreadNode* &T, char x) {// 分配空间ThreadNode *p = (ThreadNode*)malloc(sizeof(ThreadNode));// 分配空间失败的情况if(p == NULL) {return false;}// 数据域赋值p->data = x;// 将tag值设为0p->ltag = 0;p->rtag = 0;// 初始插入,无左右孩子,置空p->lchild = NULL;p->rchild = NULL;// 作为指定插入结点的左孩子T->lchild = p;return true;
}// 右插入结点
bool InsertRightTreeNode(ThreadNode* &T, char x) {// 分配空间ThreadNode *p = (ThreadNode*)malloc(sizeof(ThreadNode));// 分配空间失败的情况if(p == NULL) {return false;}// 数据域赋值p->data = x;// 将tag值设为0p->ltag = 0;p->rtag = 0;// 初始插入,无左右孩子,置空p->lchild = NULL;p->rchild = NULL;// 作为指定插入结点的左孩子T->rchild = p;return true;
}// 用于访问结点
void visit(ThreadNode *q) {// 左子树为空,建立前驱线索if(q->lchild == NULL) {// 令其左指针指向前驱q->lchild = pre;// 将tag值修改为1,表示此处左指针指向的是前驱,即左孩子指针是线索q->ltag = 1;if(pre != NULL) {printf("建立前驱线索:%c-->%c\n", q->data, pre->data);} else {printf("建立前驱线索:%c-->%s\n", q->data, "NULL");}}// 设立后继线索时前驱不能为空(无前驱无法建立后继)// 右孩子指针为空,表示可以建立后继线索if(pre != NULL && pre->rchild == NULL) {// 建立前驱结点的后继线索pre->rchild = q;// 将tag值修改为1,表示此处右指针指向的是后继,即右孩子指针是线索pre->rtag = 1;printf("建立后继线索:%c-->%c\n", pre->data, q->data);}// 线索建立完成后,前驱指针移动,指向当前指向结点处pre = q;
}// 中序线索化二叉树
// 实际上就是进行中序遍历,一边遍历一边线索化
// 因为想得到一个结点的前驱或者后继,只能在遍历时取得
// 线索化在visit函数中完成
void InThread(ThreadTree T) {if(T != NULL) {InThread(T->lchild);visit(T);InThread(T->rchild);}
}// 中序线索化二叉树
void CreateInThread(ThreadTree T) {// pre初始为NULLpre = NULL;// 若二叉树为空,则不可线索化if(T != NULL) {// 中序线索化二叉树InThread(T);// 对于最后一个结点的处理// 由于访问完成后,pre和p都指向最后一个结点// 按照线索化的要求,如果最后一个结点右孩子指向NULL,则可视为其有后继结点// 应将tag设为1if(pre->rchild == NULL) {pre->rtag = 1;printf("建立后继线索:%c-->%s\n", pre->data, "NULL");}}
}// 找到以p为根的子树中,第一个被中序遍历的结点
// 即后继结点
ThreadNode *FirstNode(ThreadNode *p) {// 循环找到最左下结点(不一定是叶子结点)// 当ltag==0时,说明其线索二叉树此结点不存在左线索,那么一定有左子树while(p->ltag == 0) {p = p->lchild;}return p;
}// 在中序线索二叉树中找到结点p的后继结点
// 要找到一个结点的后继,如果右孩子指针tag==1,直接用线索即可
// 如若不为1,那么就要根据中序遍历的特点来寻找后继结点,即左根右
// 如果该结点有右孩子且右孩子为叶子结点,那么这就是其后继结点
// 如果该节点有右孩子,且为分支结点,那么后继结点就应该是该节点右子树的最左下结点
// 注意:若没有线索,则一定存在右子树(根据线索二叉树特点)
ThreadNode *NextNode(ThreadNode *p) {// 如果右孩子结点无线索if(p->rtag == 0) return FirstNode(p->rchild);// 如果右孩子结点有线索则直接利用线索找到结点即可else return p->rchild;
}// 访问结点
void visit1(ThreadNode *T) {printf("%c   ", T->data);
}// 对中序线索二叉树进行中序遍历(利用线索非递归实现)
// 首先找到根节点最左下结点,访问之后,通过NextNode函数找后继结点访问即可
void InOrder(ThreadNode *T) {for(ThreadNode *p = FirstNode(T);p != NULL;p = NextNode(p)) {visit1(p);}
}// 找到以p为根的子树中最后一个被中序遍历的结点
// 即前驱结点
ThreadNode *LastNode(ThreadNode *p) {// 循环找到最右下结点(不一定为叶子结点)// 当rtag==0时,说明其线索二叉树此结点不存在右线索,那么一定有右子树while(p->rtag == 0) {p = p->rchild;}return p;
}// 在中序线索二叉树中找到结点p的前驱结点
// 要找到一个结点的前驱,如果左孩子指针tag==1,直接用线索即可
// 如若不为1,那么就要根据中序遍历的特点来寻找前驱结点,即左根右
// 即应该找到其左子树中遍历的最后一个结点,即左子树最右下结点,那即是其前驱结点
// 注意:若没有线索,则一定存在左子树(根据线索二叉树特点)
ThreadNode *PreNode(ThreadNode *p) {// 左子树中最右下结点if(p->ltag == 0) {return LastNode(p->lchild);} else {// 如果右孩子结点有线索则直接利用线索找到结点即可return p->lchild;}
}int main() {ThreadTree T;InitTree(T);InsertLeftTreeNode(T, 'B');InsertRightTreeNode(T, 'C');InsertLeftTreeNode(T->lchild, 'D');InsertRightTreeNode(T->lchild, 'E');InsertLeftTreeNode(T->rchild, 'F');InsertRightTreeNode(T->rchild, 'G');CreateInThread(T);printf("InOrder\n");InOrder(T);
}

4.运行结果

【数据结构】【王道】【树与二叉树】中序二叉线索树的实现及基本操作(可直接运行)相关推荐

  1. 二叉线索树的先序、中序、后序的线索化及其遍历

    线索二叉树 注意:源码 二叉线索树的概念 二叉线索树是在传统二叉树结构的基础上,加上判断结点左右孩子是否为空的标志–LTag,RTag. 当左孩子为空的时候,lchild指向该节点的前驱结点,当右孩子 ...

  2. 线索树找*p的中序后继且中序遍历 二叉线索树

    //线索树找*p的中序后继且中序遍历 二叉线索树 #define thread 1 #define link 0 typedef struct Bt{char data;struct Bt *lc;/ ...

  3. 数据结构与算法练习-二叉树中序遍历

    python数据结构与算法练习-二叉树中序遍历 二叉树中序遍历 思路 python实现 二叉树中序遍历 链接: link. 给定一个二叉树的根节点 root ,返回它的 中序 遍历. 样例 输入:ro ...

  4. 数据结构---二叉线索树

    数据结构-二叉线索树 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> typedef struct bmTree {i ...

  5. 线索二叉树中序非递归线索化以及递归线索化构建和遍历算法

    引文 大部分教材给出了 线索二叉树的中序递归线索化以及中序遍历,但是没给出非递归,现在网上大部分非递归算法代码各种条件判断写的比较离谱,所以干脆自己总结了一个清晰的.线索二叉树中序非递归线索化以及递归 ...

  6. C++数据结构与算法之二叉树中序遍历

    二叉树中序遍历 C++二叉树中序遍历基本思想 C++二叉树中序遍历代码 C++二叉树中序遍历基本思想 申请一个栈stk,再申请一个变量cur,初始值让它等于头节点 先把cur压入栈中对cur节点的整颗 ...

  7. 二叉线索树的线索化以及遍历

    //二叉树的创建,递归遍历,非递归遍历,拷贝,深度 #include<iostream> #include<stack> using namespace std; 二叉树的结构 ...

  8. 二叉树 中序遍历 python_LeetCode 105 树 从前序与中序遍历序列构造二叉树(Medium)

    17(105) 从前序与中序遍历序列构造二叉树(Medium) 描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 示例 例如,给出前序遍历 preorder = ...

  9. 数据结构学习记录(二)——折半查找二叉判定树的画法

    以下给出我在学习中总结的一种比较简便的构造折半二叉判定树的思路以及方法: 思路分析: 在计算mid值时,使用的时mid=(low+high)/2  .这里由于mid为int类型,自动默认为向下取整,因 ...

  10. 数据结构-C语言版-二叉树中序线索化

    二叉树中的线索化问题,数据结构最核心.密集的考点之一:树,以中序二叉树的线索化 代码参考王道数据结构课程! #include<stdio.h> #include<stdlib.h&g ...

最新文章

  1. ext2.2打造全新功能grid系列--仅仅动态生成GridPanel
  2. NTU 课程笔记: CV6422 regression
  3. operator模块
  4. Electron中通过globalShortcut实现监听键盘事件进而实现快捷键功能
  5. 【计网】计算机网络-物理层【理论1-2】
  6. java arraylist底层实现原理_ArrayList和LinkedList底层原理
  7. Python学习秘籍 这些窍门就连老司机都不一定知道 值得学习
  8. HttpHandler HttpModule入门篇
  9. testNG单元测试学习
  10. CodeForces 379 E New Year Tree Decorations
  11. linux chown命令: 修改文件或目录的所有者或群组
  12. VS2012下基于Glut OpenGL GL_POLYGON_STIPPLE示例程序:
  13. java----EL表达式
  14. 第一步:搭建项目基本框架
  15. 模电(十四)差分放大电路
  16. MongoDB安装(新版本保姆级教程)
  17. 目前主流的app开发方式
  18. shell入门基础知识
  19. 什么是Saas软件?
  20. 北京2008年第29届奥运会吉祥物五个福娃(组图)

热门文章

  1. 易语言.用修改注册表的方式来关闭win10自带的杀毒软件
  2. 案例集|不仅仅是酒店,还有更多可能——高星级酒店的跨界营销
  3. mac windows linux 公用磁盘格式,win访问mac分区 linuxt系统磁盘分区知识(2)
  4. 如何查看网站是否CDN加速?测试网站全国访问速度的方法!
  5. 单片机备用电池供电电路_单片机usb供电电路原理图详解
  6. Windows下Python的安装与配置
  7. 一元四次方程的求根公式
  8. 为什么很多公司不要培训出来的Java程序员?
  9. 【每日AI】什么是机器学习(ML)?
  10. 苹果手机html5定位,苹果手机常去地点可以记录多长时间?