线索二叉树

遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序序列或中序序列或后序序列。这实际上是对一个非线性序列结构进行线性化操作,使每个结点(除了第一和最后一个)在这些线性序列中有且仅有一个直接前驱和直接后继。

现在的问题是,在常用的二叉链表作为存储结构时,我们只能找到结点左右孩子的信息,而不能直接找到结点在任意序列中前驱以及后继的信息,这些信息只能通过遍历的动态过程中得到。

另一方面,在一个含有n个结点普通二叉树当中,有n+1个空链域,我们利用这些空链域来存储结点之间的前驱后继关系。在原有二叉树结点结构的基础上增加两个标志域,来判断一个结点的左右孩子指针到底指向的真的是左右子树还是前驱后继结点。

以下是线索二叉树结点的代码定义:

typedef struct BiTNode {int data;struct BiTNode* lchild;struct BiTNode* rchild;int ltag, rtag;//左右线索标志
}BiTNode,* BiTree;
BiTNode* pre = NULL;//全局变量,指向当前访问结点的前驱

线索二叉树的建立

下来,我们线索化一个二叉树。分以下三个步骤,首先建立线索:

void BuildThread(BiTree T) {//建立线索if (T->lchild == NULL) {//左子树为空,建立前驱结点T->lchild = pre;T->ltag = 1;}if (pre != NULL && pre->rchild == NULL) {//右子树为空,建立后继结点pre->rchild = T;pre->rtag = 1;}pre = T;
}

第二步,采用中序遍历的思想,遍历到每一个结点,对每一个结点建立线索,这样的建立方式叫中序线索化:

void ThreadBiTree(BiTree T) {if (T != NULL) {ThreadBiTree(T->lchild);BuildThread(T);ThreadBiTree(T->rchild);}
}

最后,整理之前两块代码,我们建立一个线索二叉树:

void BuildBiTree_Thr(BiTree T) {if (T != NULL) {ThreadBiTree(T);if (pre->rchild == NULL)pre->rtag = 1;}printf("\n线索二叉树建立完成!\n");
}

采用中序遍历线索化二叉树的方式时,需注意,最后被访问的结点无法通过第二步的递归调用被线索化。从而在第三步对最后一个被访问的结点单独进行处理。

线索二叉树的中序遍历

由于我们采用中序线索化的方式线索化了二叉树,所以在遍历的时候也应该采用中序遍历线索二叉树的方式。通过这个操作,我们可以得到线索二叉树的中序序列。

首先要如何知道一个结点P的后继位置。两种情况,直接给出结论:
1.如果P的右标志域是1,即说明P的右指针是一个线索,那么P的后继就是P的右指针所指向的结点。
2.如果P的右标志域是0,说明P这个结点有右子树,那么P的后继结点应该是P的右子树当中最左下的结点。
代码实现如下:

BiTNode* FirstNode(BiTNode* p) {//寻找最先被访问的结点while (p->ltag == 0)p = p->lchild;return p;
}
BiTNode* NextNode(BiTNode* p) {//寻找后继结点if (p->rtag == 0)return FirstNode(p->rchild);else return p->rchild;
}

结合上面的代码,我们可以实现一个采用非递归方式的遍历线索二叉树的方法:

void InOrder_Thread(BiTree T) {//中序遍历线索二叉树for (BiTNode* p = FirstNode(T);p != NULL;p = NextNode(p))visit(p);
}

类似的,我们可以采用寻找前驱的方式,得到一个逆序遍历线索二叉树的方法。原理是类似的,不做过多说明。

附加:完整代码

#include<stdio.h>
#include<stdlib.h>
#define OK          1
#define ERROR       0
#define TRUE        1
#define FALSE       0
#define OVERFLOW    -1
typedef struct BiTNode {int data;struct BiTNode* lchild;struct BiTNode* rchild;int ltag, rtag;//左右线索标志
}BiTNode,* BiTree;
BiTNode* pre = NULL;//全局变量,指向当前访问结点的前驱void CreateBiTree(BiTree* T);//创建二叉树
void BuildBiTree_Thr(BiTree T);//建立线索二叉树
void visit(BiTree T);//访问结点数据域
void BuildThread(BiTree T);//建立线索
void ThreadBiTree(BiTree T);//将二叉树线索化
BiTNode* FirstNode(BiTNode* p);//找到最先被访问到的结点
BiTNode* NextNode(BiTNode* p);//找到后继结点
BiTNode* LastNode(BiTNode* p);//找到最后被访问的结点
BiTNode* PreNode(BiTNode* p);//找到前驱结点
void InOrder_Thread(BiTree T);//中序遍历线索二叉树
void InOrder_Thread_Reverse(BiTree T);//中序逆向遍历线索二叉树
void PreOrder(BiTree T);//先序遍历:普通二叉树
void InOrder(BiTree T);//中序遍历:普通二叉树
void PostOrder(BiTree T);//后序遍历:普通二叉树void CreateBiTree(BiTree* T) {int num;scanf_s("%d", &num);if (num == 0)(*T) = NULL;else {(*T) = (BiTree)malloc(sizeof(BiTNode));if (!(*T)) {printf("\n内存分配失败\n");exit(OVERFLOW);}(*T)->data = num;(*T)->ltag = 0;(*T)->rtag = 0;CreateBiTree(&(*T)->lchild);CreateBiTree(&(*T)->rchild);}
}
void BuildBiTree_Thr(BiTree T) {if (T != NULL) {ThreadBiTree(T);if (pre->rchild == NULL)pre->rtag = 1;}printf("\n线索二叉树建立完成!\n");
}
void visit(BiTree T) {printf("%d ", T->data);
}
void BuildThread(BiTree T) {//建立线索if (T->lchild == NULL) {//左子树为空,建立前驱结点T->lchild = pre;T->ltag = 1;}if (pre != NULL && pre->rchild == NULL) {//右子树为空,建立后继结点pre->rchild = T;pre->rtag = 1;}pre = T;
}
void ThreadBiTree(BiTree T) {if (T != NULL) {ThreadBiTree(T->lchild);BuildThread(T);ThreadBiTree(T->rchild);}
}
BiTNode* FirstNode(BiTNode* p) {//寻找最先被访问的结点while (p->ltag == 0)p = p->lchild;return p;
}
BiTNode* NextNode(BiTNode* p) {//寻找后继结点if (p->rtag == 0)return FirstNode(p->rchild);else return p->rchild;
}
BiTNode* LastNode(BiTNode* p) {//寻找最后被访问的结点while (p->rtag == 0)p = p->rchild;return p;
}
BiTNode* PreNode(BiTNode* p){//寻找前驱结点if (p->ltag == 0)return LastNode(p->lchild);else return p->lchild;
}
void InOrder_Thread(BiTree T) {//中序遍历线索二叉树for (BiTNode* p = FirstNode(T);p != NULL;p = NextNode(p))visit(p);
}
void InOrder_Thread_Reverse(BiTree T) {//中序逆向遍历线索二叉树for (BiTNode* p = LastNode(T);p != NULL;p = PreNode(p))visit(p);
}
void PreOrder(BiTree T) {if (T != NULL) {visit(T);PreOrder(T->lchild);PreOrder(T->rchild);}
}
void InOrder(BiTree T) {if (T != NULL) {InOrder(T->lchild);visit(T);InOrder(T->rchild);}
}
void PostOrder(BiTree T) {if (T != NULL) {PostOrder(T->lchild);PostOrder(T->rchild);visit(T);}
}void main() {BiTree T;printf("\n输入先序序列构建二叉树(空结点用“0”表示):");CreateBiTree(&T);BuildBiTree_Thr(T);printf("\n中序遍历线索二叉树序列为:");InOrder_Thread(T);printf("\n中序逆向遍历线索二叉树序列为:");InOrder_Thread_Reverse(T);
}

总结

这样,中序线索二叉树的建立就完成了,我们利用了空链域实现了这一操作。现在的二叉树当中,原本指向空的左,右指针,现在已经指向了该结点的中序前驱,后继结点。对于那些左右指针本身不指向空的结点,我们也通过中序遍历的特点,得到了通用性的结论,得到了这些结点的后继、前驱结点的位置。

这里我们只是展示了线索二叉树的一种方式,关于二叉树的线索化,除了中序线索化,还有前序线索化和后序线索化。在后两种方式中,和中序线索化有区别,如果只是使用二叉链表,在前序线索二叉树中,我们无法找到结点的前驱结点位置;在后序线索二叉树中,我们无法找到结点的后继结点位置。解决方法有两种,采用三叉链表,或是采用遍历整个序列的老办法。

线索二叉树的线索化、及遍历相关推荐

  1. 线索二叉树,画图教你秒懂线索二叉树(线索二叉树的建立和简单操作)逻辑代码分析

    数据结构专升本学习,线索二叉树 前言 前面我们学习树和二叉树的一些基本操作,今天我们学习一个新的知识,学习一下线索二叉树,线索二叉树是由二叉链存储结构变化而来的(我们先得有个二叉链树,再做处理),就是 ...

  2. c语言线索二叉树作用,线索二叉树(C语言)

    实现下面这棵树: 先序遍历: A B C D E F 中序遍历: C B D A E F 代码 #include #include #include #include typedef enum {li ...

  3. 线索二叉树(前中后序线索化/遍历/画线索)

    线索二叉树 文章目录 线索二叉树 1 线索二叉树的基本概念 2 线索二叉树的构造 2.1 线索二叉树的存储结构 2.2 给线索二叉树画线索 2.2.1 中序 2.2.2 先序 2.2.3 后序 2.3 ...

  4. C语言实现二叉树的中序线索化及遍历中序线索二叉树

    C语言实现二叉树的线索化以及如何遍历线索二叉树! 文章目录 线索二叉树的结构及数据类型定义 根据输入结点初始化二叉树 中序遍历二叉树并线索化 遍历中序线索二叉树 项目完整代码 项目完整代码(改进版) ...

  5. 线索二叉树原理及前序、中序线索化(Java版)

    转载 原文地址:https://blog.csdn.net/UncleMing5371/article/details/54176252 一.线索二叉树原理 前面介绍二叉树原理及特殊二叉树文章中提到, ...

  6. 二叉树线索化示意图_二叉树的线索化

    二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历.二叉树作为存储结构时,一个节点只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继.为了保存这种在 ...

  7. 先序abdfcegh 中序bfdagehc 后序线索二叉树_二叉树的遍历和线索二叉树

    二叉树的遍历是指按某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,且只被访问一次. 先序遍历(NLR) 若二叉树为空,则什么也不做:否则, (1)访问根结点. (2)先序遍历左子树. (3) ...

  8. 线索二叉树(中序、先序和后序及遍历)

    链式存储 线索二叉树是二叉树的一类,在看线索二叉树之前我们先看一下二叉树的链式存储. 一个二叉树的存储例子(后面用到的二叉树都是这棵): 代码是这样的: public class BinaryTree ...

  9. 二叉树的遍历和线索线索二叉树

    一.二叉树的遍历 1.先序遍历 在二叉树非空的情况下: (1)访问根结点 (2)先序遍历左子树 (3)先序遍历右子树 对应算法如下: void PreOrder(BiTree T){   if(T ! ...

最新文章

  1. Android UI 统一修改Button控件的样式,以及其它系统控件的默认样式
  2. Android studio 第二次作业
  3. Py之pyserial:Python的pyserial库的简介、安装、使用方法之详细攻略
  4. 阅读源码学设计模式-单例模式
  5. 解读大型网站系统架构的演化
  6. [css] 说说display:none和visibility:hidden的区别
  7. picturebox与imagelist使用选择图片
  8. 4K修复版《海上钢琴师》登陆全国院线,一文读懂背后的黑科技!
  9. 诗与远方:无题(九十)
  10. 浏览器 重定向次数限制_在浏览器输入URL到页面渲染的整个流程是如何的?都有哪些步骤?...
  11. 前端那些事之日历多选插件篇
  12. 荣耀7i android版本,华为荣耀7i有几个版本?荣耀7i不同版本配置介绍
  13. SQL Server 2005 彻底卸载、重装问题
  14. 软件工程师的核心竞争力是什么
  15. Android 自定义View UC下拉刷新效果(一)
  16. 息县装修“茶几的选择”
  17. 人们怎么总跟质数过不去?
  18. JavaSE百炼成钢之新手入门(一)
  19. Surface Pro4详细拆机经验记录与分享
  20. Nginx实现XSS、SQL注入防护 —— 筑梦之路

热门文章

  1. mysql创建表的哈衣_地狱の沙汰も君次第日语谐音歌词, TV动画《鬼灯的冷彻》片头曲中文音译...
  2. 如何下载白纸坊街道卫星地图高清版大图
  3. c语言转义字符(c语言转义字符是什么意思)
  4. MAYAPlugin_Mel_顶点法线批处理_V2
  5. 什么是网络推广以及如何做好网络推广
  6. 开源海思开发板(HIVIEW开发板)
  7. DailyTask-changeLog学习手册
  8. 通过powershell安装360安全卫士
  9. python海龟漂亮图案代码大全_带有海龟图案的Python花
  10. C语言小游戏: 2048.c