我这么讲线索二叉树,我三岁大的表弟笑了笑
目录:
1.线索二叉树的由来
2.线索二叉树的概念和结构
3.二叉树的线索化
4.中序线索二叉树的代码实现
5.遍历线索二叉树
1.线索二叉树的由来
我问表弟下面的一个问题:
在n个结点一个二叉链表中,有多少个空指针域?
表弟一脸蒙蔽,于是我解释道:既然有n个结点,那么总共应该有2n个指针域,我们的根结点是无结点指向的,其他的结点肯定都有一个结点指向,也就是说一共用了n-1个指针域(根结点无结点指向所以需要减1),那么也就是说还有n+1个指针域没有使用,由此我们可以得出一个结论:
任何一颗含n个结点的二叉树都有n+1个指针域是空指针域
那么这么多的空指针域造成浪费 ,表弟你心疼不,表弟:管我屁事
我:
为了解决这些浪费的空指针域,我们引进线索二叉树
2.线索二叉树的概念和结构
2.1线索二叉树的概念
表弟:你给我讲讲线索二叉树呗
我:你以为我和你搞着玩?讲就讲come on baby
线索二叉树实际上就是使用这些空指针域来存储结点之间前趋和后继关系的一种特殊的二叉树。线索二叉树中,如果结点有左子树,则lchild 指针域指向左孩子,否则lchild 指针域指向该结点的直接前趋;同样,如果结点有右子树,则rchild 指针域指向右孩子,否则rchild 指针域指向该结点的直接后继。
LTag 和RTag 为标志域。实际上就是两个布尔类型的变量:
LTag 值为0 时,表示lchild 指针域指向的是该结点的左孩子;为1 时,表示指向的是该结点的直接前趋结点;
RTag 值为0 时,表示rchild 指针域指向的是该结点的右孩子;为1 时,表示指向的是该结点的直接后继结点。
2.2线索二叉树的结构代码实现
表弟:你这讲的花里胡哨的,裸敲个线索二叉树的结构代码我看看
我:你这是质疑你表哥我的实力啊,上DEVC++
#define TElemType int//定义元素的数据类型
typedef struct BiThrNode{//线索二叉树那稳的是链式结构TElemType data;struct BiThrNode *lchild,*rchild;int LTag;int Rtag;
}BiThrNode,*BiThrTree;
3.二叉树的线索化
表弟:那到底怎样建立一个线索二叉树呢
我:别急啊,听我慢慢说
将二叉树转化为线索二叉树,实质上是在遍历二叉树的过程中,将二叉链表中的空指针改为指向直接前趋或者直接后继的线索。
线索化的过程即为在遍历的过程中修改空指针的过程。在遍历过程中,如果当前结点没有左孩子,需要将该结点的lchild 指针指向遍历过程中的前一个结点,所以在遍历过程中,设置一个指针(名为pre ),时刻指向当前访问结点的前一个结点。
表弟:空指针域里存放这些前驱和后继有啥用,仅仅就是为了不浪费,但是存放了没有用的东西和浪费不是差不多吗
我:这个问题问的好,其实存放前驱和后继的意义是:
线索二叉树的建立过程其实就是提高遍历二叉树效率的过程
表弟:这话我TM听不懂,说人话
我:卧槽你个**,多大点说对表哥我说脏话,你这是上天啊
表弟:可不是要上天,你赶紧讲讲刚那句话啥意思
我:其实你想,我们根据普通的二叉树的某一个结点是不是只能找到它的左孩子和右孩子,我们想要知道某一个结点的前驱和后继,是不是就要重新遍历整个二叉树,也就是说我们每一次想要知道某一个结点的前驱和后继,都要遍历一遍整个二叉树,但是我们如果创建的是线索二叉树,那么它的空指针域里就存放它的前驱和后继的信息,遍历起来就特别的方便,就不需要遍历整棵二叉树,就大大提高了遍历的效率。
注意我们以不同的方式遍历二叉树它的前驱和后继也就不同,所以线索二叉树又分为前序线索二叉树,中序线索二叉树,后续线索二叉树
其实我们构建一颗线索二叉树的过程其实就是,等同是把一颗二叉树变成一个双向链表的过程,这怎么理解呢,下面是一颗二叉树的模型
我们中序遍历利用指向右孩子的空指针(指向后继)
再利用指向左孩子的空指针(指向前驱)
联合起来就变成如下图(是不是类似一个双向链表)
4.详解中序线索二叉树
表弟:那怎么用代码实现遍历过程中让一颗二叉树变成线索二叉树呢
我:我教你用遍历中序二叉树的方法构造一个中序线索二叉树,看下面一段代码
BiThrTree pre;//所以在遍历过程中,设置一个全局变量pre,时刻指向当前访问结点的前一个结点。
void InThreading(BiThrTree p)//以p为根的子树中序线索化
{//如果当前结点存在if (p){//递归当前结点的左子树,进行线索化InThreading(p->lchild); //如果当前结点没有左孩子,左标志位设为1,左指针域指向上一结点preif (!p->lchild)//p的左孩子为空 {//前驱线索p->Ltag =1;//左孩子指针指向前驱p->lchild = pre; }elsep->LTag=0; //如果pre 没有右孩子,右标志位设为1,右指针域指向当前结点。if (!pre->rchild){//后继线索pre->Rtag =1;//前驱右孩子指针指向后继(当前结点p)pre->rchild =p;}else pre->RTag=0;pre = p; //pre 指向当前结点InThreading(p->rchild); //递归右子树进行线索化}
}
还有一种情况,那就是带头结点的中序线索二叉树的构建方法,那么什么是带头结点的二叉树呢
我们还拿这颗二叉树来说:
把上述二叉树转变成线索二叉树后会发现H结点无前驱,G结点无后继,这时候引入一个头结点
那么我们怎样线索化带头结点的线索二叉树呢,看下面这一段代码
//建立头指针,使其左指针指向根结点,右指针指向遍历的最后一个结点
void InOrder_Thr(BiThrTree &Thr,BiThrTree T)//中序遍历二叉树T,并将其中序线索化,Thr为头指针指向头结点
{Thr=new BiThrNode; //建立一个头结点 Thr->LTag=0;//头结点有左孩子若树非空那么左孩子指向根结点 Thr->RTag=1;//右孩子指针为右索引 Thr->rchild=Thr; //初始化的时候右指针指向自己 if(!T)//如果树为空那么初始化的时候指针也指向自己 Thr->lchild=Thr;else{Thr->lchild=T;//头结点左孩子指向根,pre初始值为头结点 InThreading(T);//对以T为根的二叉树进行线索化 pre->rchild=Thr;//中序线索化后pre为遍历的最后结点 ,让最后的结点的右结点指向头结点 pre->RTag=1;Thr->rchild=pre;//头结点的右索引指向pre }
}
5.遍历线索二叉树
下图中是一个按照中序遍历建立的线索二叉树。其中,实线表示指针,指向的是左孩子或者右孩子。虚线表示线索,指向的是该结点的直接前趋或者直接后继。
使用线索二叉树时,会经常遇到一个问题,如上图中,结点8的直接后继直接通过指针域获得,为结点5;而由于结点5的度为2 ,无法利用指针域指向后继结点,整个链表断掉了。当在遍历过程,遇到这种问题是解决的办法就是:寻找先序、中序、后序遍历的规律,找到下一个结点。
在先序遍历过程中,如果结点因为有右孩子导致无法找到其后继结点,如果结点有左孩子,则后继结点是其左孩子;否则,就一定是右孩子。拿上图举例,结点2的后继结点是其左孩子结点4 ,如果结点4不存在的话,就是结点5 。
在中序遍历过程中,结点的后继是遍历其右子树时访问的第一个结点,也就是右子树中位于最左下的结点。例如上图中结点5,后继结点为结点11,是其右子树中位于最左边的结点。反之,结点的前趋是左子树最后访问的那个结点。
后序遍历中找后继结点需要分为3 种情况:
1. 如果该结点是二叉树的根,后继结点为空;
2. 如果该结点是父结点的右孩子(或者是左孩子,但是父结点没有右孩子),后继结点是父结点;
3. 如果该结点是父结点的左孩子,且父结点有右子树,后继结点为父结点的右子树在后序遍历列出的第一个结点。
使用后序遍历建立的线索二叉树,在真正使用过程中遇到链表的断点时,需要访问父结点,所以在初步建立二叉树时,宜采用三叉链表做存储结构。
表弟:这么多,劝退?
我:我们重点掌握中序的就行,下面我实现一下中序线索二叉树的遍历的非递归代码你看看
void InOrderTraverse_Thr(BiThrTree T)
{//T为头指针,头结点的左链lchild指向根结点BiThrTree p=T->lchild;//p指向根结点 while(p!=T)//空树或者遍历结束时p==T {while(p->Ltag==0)p=p->lchild;//沿着左孩子向下 cout<<p->data;//访问左子树为空的结点 while(p->Rtag==1&&p->rchild!=T){p=p->rchild;//沿着右线索访问后继结点 cout<<p->data;}p=p->rchild;//转向p的右子树 }
}
结合下面图一起看,下面图的中序遍历结果时4 2 8 5 11 9 12 1 6 3 7
我这么讲线索二叉树,我三岁大的表弟笑了笑相关推荐
- 编程基础 - 线索二叉树 (Threaded Binary Tree)
编程基础 - 线索二叉树 (Threaded Binary Tree) 返回分类:全部文章 >> 基础知识 返回上级:编程基础 - 二叉树 (Binary Tree) 本文将介绍线索二叉树 ...
- c++ 实现线索二叉树
本代码主要参考了大话数据结构的思想,由于其为c语言版,所以本人对其中一些函数进行了封装,并增加了一些新的功能. 由于本代码的基本思想在大话数据结构书中已经说的很清楚了,所以此处就不再进行说明代 ...
- 数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉 ...
- 【Java数据结构与算法】第十一章 顺序存储二叉树、线索二叉树和堆
第十一章 顺序存储二叉树.线索化二叉树.大顶堆.小顶堆和堆排序 文章目录 第十一章 顺序存储二叉树.线索化二叉树.大顶堆.小顶堆和堆排序 一.顺序存储二叉树 1.介绍 2.代码实现 二.线索二叉树 1 ...
- 线索二叉树,画图教你秒懂线索二叉树(线索二叉树的建立和简单操作)逻辑代码分析
数据结构专升本学习,线索二叉树 前言 前面我们学习树和二叉树的一些基本操作,今天我们学习一个新的知识,学习一下线索二叉树,线索二叉树是由二叉链存储结构变化而来的(我们先得有个二叉链树,再做处理),就是 ...
- 【数据结构Note5】- 树和二叉树(知识点超细大全-涵盖常见算法 排序二叉树 线索二叉树 平衡二叉树 哈夫曼树)
文章目录 5.1 树和二叉树引入 5.1.1 树的概念 5.1.2 树的表示 5.1.3 树中基本术语 5.2 二叉树 5.2.1 概念 5.2.2 二叉树的性质 5.2.3 特殊的二叉树 5.2.4 ...
- 抖音出现大量“三岁用户”,马化腾李彦宏都被还童
金磊 发自 凹非寺 量子位 报道 | 公众号 QbitAI 互联网大佬们小时候,会长什么样子? 嗯,是个有趣的问题. 不妨上手试一试,先来看下马化腾. 虽然年纪变小了不少,但依旧保持着他的神貌,只是多 ...
- l2-004 这是二叉搜索树吗?_LeetCode 例题精讲 | 11 二叉树转化为链表:二叉树遍历中的相邻结点...
本期例题: LeetCode 98. Validate Binary Search Tree 验证二叉搜索树(Medium) LeetCode 426. Convert Binary Tree to ...
- 【线索二叉树详解】数据结构06(java实现)
线索二叉树 1. 线索二叉树简介 定义: 在二叉树的结点上加上线索的二叉树称为线索二叉树. 二叉树的线索化: 对二叉树以某种遍历方式(如先序.中序.后序或层次等)进行遍历,使其变为线索二叉树的过程称为 ...
最新文章
- #论文 《Deep Residual Learning for Image Recognition》
- XML file does not appear to have any style information associated with it. XHTML程序出现这个错误
- STM32/M3/M0关于开关总中断的问题
- 仿真器如何工作以及如何编写? [关闭]
- 漫画:什么是桶排序?
- https://blog.csdn.net/Darryl_Tang/article/details/80545688
- 大学综评自招面试 计算机专业,自主招生中的综合评价面试技巧
- 语音助手为什么需要搜索?
- 卷积神经网络图像分类识别
- 微信小程序:小程序内用户帐号登录规范调整和优化建议
- 苹果授权登录绑定手机号被拒绝
- Hadoop系列之-7、Hadoop3.x的介绍
- [leetcode] Ugly Number II
- python定位二维码_python实现二维码、条形码识别
- C++洛谷题解(8)
- 戴尔微型计算机电池位置,转:戴尔笔记本电池拆解教程-看看电池内部的样子
- 每周教育关注 | 新时代的教育评价改革
- 百张图片教你入手第一个PCB项目,并免费打板
- 开关数字方格哥德巴赫猜想自我数积木
- 迅游加速器对ipv6访问的影响