您的位置:IT爆料网 > 互联网

【数据结构之线索二叉树】线索二叉树的原理及创建

发布时间:2022-12-10 05:26:27  来源:互联网     背景:

本文转载自微信公众号「二十二画程序员」,作者行小观。转载本文请联系二十二画程序员公众号。  

 1. 为什么要用到线索二叉树?

我们先来看看普通的二叉树有什么缺点。下面是一个普通二叉树(链式存储方式):

一颗普通二叉树

乍一看,会不会有一种违和感?整个结构一共有 7 个结点,总共 14 个指针域,其中却有 8 个指针域都是空的。对于一颗有 n 个结点的二叉树而言,总共会有 n+1 个空指针域,这个规律使用所有的二叉树。

这么多的空指针域是不是显得很浪费?我们学习数据结构和算法的重点就是在想法设法地提高时间效率和空间利用率。这么多的指针域就这么白白浪费了,太败家了!

所以我们要想法子好好利用它们,利用它们来帮助我们更好地使用二叉树这个数据结构。

那么如何利用呢?

前面已经强调过很多次了,遍历二叉树的实质是将二叉树中非线性结构的结点转化为线性的序列,然后才能方便我们遍历。

比如上图的中序遍历序列为:DBGEACF。

对于一个线性序列(线性表)来说,它有直接前驱和直接后继的概念(在【什么是线性表?】中介绍过)。比如在中序遍历序列中,B 的直接前驱为 D,直接后继为 G。

我们之所以能知道 B 的直接前驱和直接后继,是因为我们按照中序遍历的算法,把二叉树的中序遍历序列写出来了,然后根据这个顺序序列说谁的前驱是谁、后继是谁。

直接前驱和直接后继是不能完全直接通过二叉树得到的,因为二叉树中只有双亲和孩子结点之间的直接关系,即二叉树的结点指针域中只存储了其孩子结点的地址。

现在的需求是,我想能直接从二叉树上得到某结点在中序遍历方式下的直接前驱和直接后继。

这时候就需要用到线索二叉树了。

2. 什么是线索二叉树?

当然,我们肯定需要借助结点的指针域来保存直接前驱和直接后继的地址。

其实,在上图的普通二叉树中(以中序遍历得到的序列),部分结点(指针域不为空的结点)是可以找到其直接前驱或后继的,比如结点 E 的左孩子 G 就是结点 E 的直接前驱;结点 A 的右孩子 C 就是结点 A 的直接后继。

但部分结点(指针域为空)是行不通的,比如结点 G 的直接后继是 E,直接前驱是 B,但在二叉树中却不能得出这样的结论。怎么办呢?我们注意到,结点 G 的两个指针域都为 NULL,并未被利用,那么我们使用这两个指针,分别指向其前驱和后继不就好了吗?

中序遍历下结点G的指向情况

实在是两全其美,天作之合!但是问题并没有解决!

因为我们是利用空指针域来指向前驱或后继的,对于那些指针域不为空的结点,这样是矛盾的,比如结点 E 和结点 B。

既然有矛盾,那么我们就发现产生矛盾的根源,解决矛盾。

产生矛盾的根源是:结点的指针域为空和不为空时,指针的指向矛盾。即,指针不为空时指向孩子和指针为空时指向前驱或后继之间的矛盾。

那么我们对症下药,把指针域为空和不为空给区分出来,清晰地告诉指针:不为空时指向孩子,为空时指向前驱或后继。这就需要我们给两个指针各添加一个标志位。

线索二叉树的结点

并约定以下规则:

left_flag == 0 时,指针 left_child 指向左孩子 left_flag == 1 时,指针 left_child 指向直接前驱 right_flag == 0 时,指针 right_child 指向右孩子 right_flag == 1 时,指针 right_child 指向直接前驱

二叉树的结点要有所变化:

/*线索二叉树的结点的结构体*/ typedef struct Node {     char data; //数据域     struct Node *left_child; //左指针域     int left_flag; //左指针标志位     struct Node *right_child; //右指针域     int right_flag; //右指针标志位 } TTreeNode; 

有了标志位,一切就能理清了。我们称指向直接前驱和后继的指针为线索。标志位为 0 的指针是指向孩子的指针,标志位为 1 的指针是线索。

一个二叉链表树,结点结构如上,我们将所有空指针都变为线索,这样的二叉树就是二叉线索树。

3. 如何创造线索二叉树?

在普通二叉树中,我们想要获取某个结点在某种遍历次序下的直接前驱或后继,每次都需要遍历获取到遍历次序之后才能知道。而在线索二叉树中,我们只需要遍历一次(创造线索二叉树时的遍历),之后,线索二叉树就能“记住”每个结点的直接前驱和后继了,以后都不需要再通过遍历次序获取前驱或后继了。

我们按照某种遍历方式,把普通二叉树变为线索二叉树的过程被称为二叉树的线索化。

接下来,我们用中序遍历的方式,将下面的二叉树线索化为线索二叉树

将标志位为 1 的指针,按照中序遍历序列,使其指向前驱或后继:

其中,结点 D 没有直接前驱,结点 F 没有直接后继,故指针为 NULL。

到此,我们算是解决了拥有 n 个结点的二叉树存在 n+1 个空指针域所造成的浪费,解决方式是给每个结点的指针增加一个标志位,以此来利用空指针域。标志位中存储的是 0 或 1 的布尔值,与浪费的空指针域相比,是相对比较划算的。而且使二叉树具有了一种新特性——二叉树中能保存在某种遍历次序下的结点之间的前驱和后继关系。

4. 线索化的实现

请注意一点,线索二叉树是由普通二叉树得来的,而且是按某种遍历顺序得来的。因为线索是在知道某个结点的前驱和后继的情况下才能设置,而前驱和后继关系不能通过二叉树直接体现,只能通过遍历二叉树得到的线性序列得出关系。所以要通过某种遍历方式得到具有前驱和后继关系的序列后,才能修改结点的空指针,进而设置线索。

即:线索化的实质是在按照某种遍历次序进行遍历二叉树的过程中修改结点的空指针,使其指向其在该遍历次序下的直接前驱或直接后继的过程。

我们在【二叉树的遍历原理】和【二叉树的遍历实现】分别介绍了二叉树四种遍历方式的原理及代码实现。当时我们是以打印为例来介绍遍历的。但遍历不止做打印的事,还可以做线索化的事。

所以,代码的大体结构还是一样的,我们只需把遍历代码中的打印代码换成线索化的代码,并作出一些其他改变即可。

下面以下图为例,分别介绍三种线索化:

一颗未线索化的二叉树,其所有标志位均默认为 0.

示例

4.1. 中序线索化

按照中序遍历次序线索化后,可得下图:

我们先再次明确以下内容:

我们是在遍历二叉树的过程中进行线索化的。 中序遍历的顺序为:左子树 >> 根 >> 右子树。 线索化修改两个东西:空指针域和其对应的标志位。 如何修改?将空指针域置为直接前驱或后继。

所以我们的问题变成了:

找到所有空指针域。 找到空指针域所属结点,在先序次序下的直接前驱和直接后继。 修改空指针域的内容,及其标志位,使该指针称为线索。

说明:我们在遍历二叉树时,使用到了递归,所以在进行线索化的时候,也会使用它。

具体代码如下:

//全局变量 prev 指针,指向刚访问过的结点 TTreeNode *prev = NULL;  /**  * 中序线索化  */ void inorder_threading(TTreeNode *root) {     if (root == NULL) { //若二叉树为空,做空操作         return;     }     inorder_threading(root->left_child);     if (root->left_child == NULL) {         root->left_flag = 1;         root->left_child = prev;     }     if (prev != NULL && prev->right_child == NULL) {         prev->right_flag = 1;         prev->right_child = root;     }     prev = root;     inorder_threading(root->right_child); } 

4.2. 先序线索化

按照先序顺序线索化后,可得下图:

具体代码如下:

// 全局变量 prev 指针,指向刚访问过的结点 TTreeNode *prev = NULL;  /**  * 先序线索化  */ void preorder_threading(TTreeNode *root) {     if (root == NULL) {         return;     }     if (root->left_child == NULL) {         root->left_flag = 1;         root->left_child = prev;     }     if (prev != NULL && prev->right_child == NULL) {         prev->right_flag = 1;         prev->right_child = root;     }     prev = root;     if (root->left_flag == 0) {         preorder_threading(root->left_child);     }     if (root->right_flag == 0) {         preorder_threading(root->right_child);     } } 

4.3. 后序线索化

按照后序遍历次序线索化后,可得下图:

具体代码如下:

//全局变量 prev 指针,指向刚访问过的结点 TTreeNode *prev = NULL;  /**  * 后序线索化  */ void postorder_threading(TTreeNode *root) {     if (root == NULL) {         return;     }     postorder_threading(root->left_child);     postorder_threading(root->right_child);     if (root->left_child == NULL) {         root->left_flag = 1;         root->left_child = prev;     }     if (prev != NULL && prev->right_child == NULL) {         prev->right_flag = 1;         prev->right_child = root;     }     prev = root; }  5. 总结

线索二叉树充分利用了二叉树中的空指针域,给予二叉树一个新特性——通过一次遍历进行线索化后,二叉树中就能保存其结点之间的前驱和后继关系。

所以,如果我们需要频繁遍历二叉树,查找某个结点的直接前驱或后继结点,使用线索二叉树是非常合适的。

此外,由于代码涉及到递归,初次接触可能不好理解,我们可以借助断点进行调试,细致观察线索化的整个过程来帮助理解。

 


本文标题:【数据结构之线索二叉树】线索二叉树的原理及创建 - 互联网
本文地址:www.itbaoliao.com/hlw/11015.html

返回网站首页

本文评论
ces2015展会第一天:自动驾驶成焦点(图)
ces2015展会第一天:自动驾驶成焦点(图)CES上海云集了众多科技厂商,大量泛科技产品悉数亮相,除独轮车和无人机这种酷玩以外,汽车和互联网的结合才是此次展会亮点所在。其中奥迪R8选择在控制中枢上选择和百度、移动/联通进...
日期:11-29
关于人机世纪大战 你应该知道这些
距离“人机世纪大战”的时间越来越近了,今日“人工智能是否超越人类”的谜底即将揭晓。当人类与自己制造出来的机器斗智斗勇时,输赢之间似乎已不再是简单的技术问题了。让我们先来了解一下这场人机世纪大战的概况。...
日期:10-10
索尼XZ2港版售价公布:索尼XZ2港版配置一览
上个月的MWC大展上,索尼带来了期待已久的Xperia XZ2和XZ2 Compact一大一小两款骁龙845全面屏旗舰。日前,香港媒体公布了Xperia XZ2和XZ2 Compact的售价。其中,Xperia XZ2定价6198港币(约合5005元),3月13日开启预定,3月23日正式开卖。Xperia XZ2 Compact定价4998港币(约合4036元),同样是3月23日开卖。...
日期:12-08
女黑客一年不洗澡:1年不洗澡不出门惊呆网友
去年,借贷宝“裸条”借贷事件闹得沸沸扬扬,而有一名做借贷宝的女黑客,竟然也干起了盗刷银行卡的勾当,每天在网上聊天超过20小时,1年不洗澡不出门。...
日期:10-24
捡包归还被要车费引争议 寻失主却被索要车费!
拾金不昧是美德,做好事至少也应得到失主的感激,但是最近大连女子于瑶在南京的奇特遭遇,却让众多感到无法理解。...
日期:11-29
铭万网:把握多元化和专业化平衡是关键命题
易观分析:铭万网为企业客户提供的“一揽子”解决方案集中体现在其主打产品“八方通宝”中。该产品包含了四种主要服务,其一是为企业提供部分定制化的自建网站功能;其二是网站联盟服务;其三是应用服务;其四是管理工具。...
日期:12-08
苹果发布iTunes9.1支持iPad
据国外媒体报道,本周六,苹果新品iPad平板电脑即将上市,为此苹果特意对媒体播放软件和管理软件iTunes进行了更新,推出iTunes 9.1版本软件对iPad提供支持。  据了解,用户可以直接通过苹果或通...
日期:10-07
王思聪发微博骂自家酒店 网友:生起气来我连自己都骂
王思聪近日在长沙录制直播节目《Hello 女神》,昨晚录制节目后王思聪下榻长沙万达文华酒店,然而在凌晨,王公子怒发微博,“长沙万达酒店是他住过的最(和谐)的酒店,还(和谐)是自己开的!...
日期:12-05
纯网综艺《大学生来了》播放破亿  爱奇艺引爆95后圈层文化
在经历2015年纯网内容元年爆发之后,纯网市场一直不乏亮眼题材。5月28日,由爱奇艺打造的极致青春观点秀《大学生来了》总播放量突破亿次大关,截至目前,总播放量高达1.04亿。在凭借《奇葩说》、《偶滴歌神啊》等优质纯网内容抢占85后、90后用...
日期:11-09
苏宁闪拍闪瞎眼iPhone5S拍出13万 将设拍卖价格上限
今日凌晨0点,备受网友期待的苏宁易购“0元闪拍”活动出现疯狂一幕:一款原价为4518元的深空灰苹果手机iPhone5S (16GB)竟然拍出136100元的天价,这让不少网友唏嘘不已。对此,苏宁云商集团运营总部执行副...
日期:10-07
淘宝日本雅虎网购平台上线 称价格较传统低60%
淘宝与日本雅虎共建的网购平台今日推出,面向中国买家的淘日本及面向日本买家的中国商城同时上线,据淘宝网估算,在淘日本上购买商品价格将比通过传统渠道从海外购买降低60%。...
日期:12-01
戈恩律师回应:仍握有戈恩护照 不可能用于出逃
前日产汽车(7201.T)和雷诺汽车(Renault)(RENA.PA)董事长戈恩的一名律师周二向记者表示,他的律师团仍持有戈恩的三本护照,不可能利用其中任何一本护照逃离日本,该名律师并称他的客户的行为“无可饶恕”。...
日期:10-01
朱广权段子版春节祝福:朗朗上口,寓意很好,立意新颖,很高级
【TechWeb】2月12日消息,大年初一,央视节目主持人朱广权在节目中为观众送上牛年第一条春节祝福 :新的一年,三餐四季、温柔有趣、牛年日日奔红火,天天大吉大利!天是2021初一,段子手朱广权又来啦。今年他说了一段段子版的拜年文案。文案朗...
日期:12-05
1元水消失背后:康师傅被挤出前三甲 农夫山泉等狂涨价
1元瓶装水消失了,康师傅被挤出了品牌前三甲,这背后意味着什么?农夫山泉、怡宝、百岁山……各种品牌应有尽有,零售价2元起。“一块钱的水几乎消失了。”柳芳对《财经天下》周刊说道,她已经记不起自己上次喝一块钱的水是什么时候了。“上学常喝的康师傅,...
日期:09-29
QQ音速甄爱誓约闪现沉船 预热粉色情人节
龙年早春,元宵节未过,214粉色情节人的浓浓甜蜜气息已顺着春风扑鼻而来。而情人节作为情侣间亘古不变爱情见证的重要节日,QQ音速也定会为大家送上多重精彩的节日豪礼,来帮住你逃脱红男绿女间表达爱你在心口难开的窘境,与心中的TA共赴甜蜜爱情之旅。...
日期:11-27
丢了手机也不怕 金山隐私保险箱试用
金山隐私保险箱是由金山网络推出的一款专业加密照片、视频,贴心保护手机隐私的手机加密软件,是移动设备上最专业的隐私加密保护工具,提供照片、视频、文件及文件夹加密功能。它能够全面保护你的私密文件,让你的隐私再无后顾之忧。是你用手机存放一些保密资料或是不方便让人知道的资料的绝佳选择。...
日期:11-20
IT巨头的青春岁月:乔布斯老照片曝光(组图)
一个海盗,一个偏执狂,一个将艺术和科技完美结合的IT领袖,一个改变了世界的人。伯乐在线精挑细选乔布斯的一些照片,包括孩提时代、小学、中学、大学、初次创业前后、第二次创业和回归苹果。每一张照片都是一个故事,每一张照...
日期:11-22
360网站卫士联手防黑联盟发起“防黑进行时”活动
北京时间11月6日消息,由国内最大的互联网安全供应商360旗下360网站卫士和“中国网站安全防黑联盟”联合举办的“防黑行动进行时”活动正在火热进行中。即日至30日活动期间,站长只需将360网站...
日期:12-07
马克龙用四国语言拜年 庆祝农历新年
【TechWeb】2月13日消息,法国总统马克龙于当地时间11日晚在社交媒体上用法语、中文、韩语及越南语向庆祝农历新年的人们拜年。他用中文写道:“向所有庆祝农历新年的人们致以我最美好的祝愿,祝愿大家健康、成功、幸福!&rdq...
日期:10-24
2D《核金风暴》网吧专服独享VIP特权
核金风暴 CGWR 得分CGWR:193 位CGWR介绍7.4五爪雷龙旗下年度2D横版射击力作《核金风暴》,自9月15日精英首测开启,受到媒体、渠道、玩家等各方关注。为了更好得进行游戏的宣传与推广,五爪雷龙正积极准备与网吧的深度合作,上...
日期:11-21