数据结构(超详细讲解!!)第二十五节 线索二叉树

1.线索二叉树的定义和结构  

问题的提出:

通过遍历二叉树可得到结点的一个线性序列,在线性序列中,很容易求得某个结点的直接前驱和后继。但是在二叉树上只能找到结点的左孩子、右孩子,结点的前驱和后继只有在遍历过程中才能得到,那么,如何保存遍历二叉树后动态得到的线性序列,以便快速找到某个结点的直接前驱和后继?  

分析:    

n个结点有n-1个前驱和n-1个后继;    

一共有2n个链域,其中:n+1个空链域,n-1个指针域;    

因此, 可以用空链域来存放结点的前驱和后继。    线索二叉树就是利用n+1个空链域来存放结点的前驱和后继结点的信息。

定义:

规定,若结点有左孩子,则其lchild指示其左孩子,否则,令lchild域指示其前驱;若结点有右孩子,则其rchild指示其右孩子,否则,令rchild域指示其后继。为了表示lchild和rchild域指向的是左、右孩子还是前驱、后继,可以加两个标志域,以明确lchild和rchild的指向。  

结点结构:

 若结点有左子树,则左链域lchild指示其左孩子(ltag=0);否则,令左链域指示其前驱(ltag=1);    

若结点有右子树,则右链域rchild指示其右孩子(rtag=0);否则,令右链域指示其后继(rtag=1);

//线索二叉树的类型定义:
typedef  struct BiThrNode
{	ElemType data ;struct BiThrNode * lchild , * rchild ;int ltag , rtag ;
}BiThrNode , * BiThrTree ;

其中:

ltag  = 0     lchild域指示结点的左孩子      

ltag =  lchild域指示结点的前驱     

rtag = 0    rchild域指示结点的右孩子

  以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向前驱和后继的指针,叫做线索(Thread)。加上线索的二叉树叫做线索二叉树(Thread Binary Tree)。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。

 为了操作方便,在存储线索二叉树时增设一个头结点,其结构与其他的线索二叉树的结点相同,只是数据域不存储任何数据,其左指针域指向二叉树的根结点,右指针域指向遍历的最后一个结点,而二叉树在某种遍历下的第一个结点的前驱和最后一个结点的后继线索都指向头结点。 

2.二叉树的线索化

基本思想:        

二叉树的线索化实质上是遍历一棵二叉树。在遍历过程中,访问结点的操作是检查此结点的左、右指针域是否为空,如果为空,将它改为指向其前驱或后继结点的线索。

本节以中序线索化为例说明其线索化过程。        

为实现这一过程,设指针p指向当前结点,pre始终指向刚刚访问过的结点,即p的前驱,以便于修改pre的后继线索和p的前驱线索。在线索化算法中访问当前结点p的处理方法如下:        

①若结点p的左指针域为空,则将其标志位置为1,并使 p->lchild指向中序前驱结点pre(即左线索化);        

②若结点pre的右指针域为空,则将其标志位置为1,并使pre->rchild指向中序后继结点p(即右线索化);        

③将pre指向刚刚访问过的结点p(即pre=p),线索化p的右子树。

实现算法如下:

BiThrTree pre ;		/* 全局变量pre始终指向刚访问的结点*/
int InOrderThread ( BiThrTree * head , BiThrTree bt )
{	*head = (BiThrTree)malloc(siazeof( BiThrNode)) ;if ( * head == NULL ) return 0;/* 线索化时头结点空间分配失败,返回 */( * head ) -> ltag = 0 ; ( * head ) -> rtag = 1 ;( * head ) -> rchild = * head ;	/* 右指针回指 */if ( bt == NULL ) ( * head ) -> lchild = * head ;/* 空二叉树左指针回指 */else {	( * head ) -> lchild = bt;  /*头结的左孩子指针指向二叉树的根结点*/pre = * head ;InThreading ( bt ) ;		/* 中序遍历二叉树并线索化 */pre -> rchild = * head ;pre -> rtag = 1 ;		/* 最后一个结点线索化 */( * head ) -> rchild = pre ;}return 1 ;
}
void InThreading ( BiThrTree p )
{	if ( p ){     InThreading ( p-> lchild ) ;if(p->lchild==NULL)   /* p无左孩子,左指针域为线索 */{   p->ltag=1 ; 		p–>lchild=pre ;  }if (pre->rchild==NULL)    /*pre无右孩子,其右指针域为线索*/{   pre->rtag=1;	pre->rchild=p;  }	pre = p ;InThreading ( p -> rchild ) ;}
}

线索二叉树中结点的前驱和后继查找

1、中序线索二叉树中结点p的中序前驱结点      

对于中序线索二叉树上的任一节点p,查找其中序的前驱结点有下面两种情况:      

(1)若该结点的ltag = 1,那么其左指针域指向的结点就是结点p的前驱结点。    

(2)若该结点的ltag = 0,则该结点有左孩子,根据中序遍历的定义,其前驱结点是以该结点的左孩子为根结点的子树的最右、最下结点(中序遍历该子树时最后一个访问结点),即沿着其左子树的右指针域向下查找,当某结点的右标志域为1时,它就是所要找的前驱结点。

//中序线索二叉树查找结点的前驱节点算法如下:
BiThrTree InPreNode(BiThrTree p)
{	BiThrTree pre;pre = p->lchild;if(p->ltag != 1)		/* 结点p有左孩子*/while(pre->rtag == 0) 	pre = pre->rchild;/* 从左子树的根结点开始,沿右指针域往下查找,直到没有右孩子为止 */return pre;			/* 返回结点p的前驱结点*/
}

2、中序线索二叉树中结点p的中序后继结点      

对于中序线索二叉树中的任一节点p,查找其中序的后继结点有下面两种情况。      

(1)如果该结点rtag = 1,则其右指针域指向的结点就是结点p的后继结点。      

(2)如果该结点rtag = 0,则该结点有右孩子,根据中序遍历的定义,它的后继结点是以该结点的右孩子为根结点的子树的最左、最下结点(中序遍历该子树时第一个访问的结点),即沿着其右子树的左指针域向下查找,当某结点的左标志域为1时,它就是所要找的后继结点。

//中序线索二叉树查找结点的后继节点算法如下:
BiThrTree InPostNode(BiThrTree p)
{	BiThrTree post;post = p->rchild;if(p->rtag != 1)	/* 结点p有右孩子*/while(post->ltag == 0)	post = post->lchild;/* 从右子树的根结点开始,沿左指针域往下查找,直到没有左孩子为止 */return post;		/* 返回结点p的后继结点*/
}

3.思考

1.如何在先序线索树中查找结点p的后继?

 在先序线索树中找结点的后继比较容易,根据先序线索树的遍历过程可知:      

若结点p存在左子树,则p的左孩子结点即为p的后继;    

 若结点p没有左子树,但有右子树,则p的右孩子结点即为p的后继;      

若结点p既没有左子树,也没有右子树,则结点p的RChild指针域所指的结点即为p的后继。

用语句表示则为:

if (p->Ltag==0) succ=p->LChild else succ=p->RChild

2.在先序线索树中如何找结点的前驱?

若结点p是二叉树的根,则p的前驱为空; 若p是其双亲的左孩子,或者p是其双亲的右孩子并且其双亲无左孩子,

则p的前驱是p的双亲结点;

若p是双亲的右孩子且双亲有左孩子,

则p的前驱是其双亲的左子树中按先序遍历时最后访问的那个结点。

4.删除插入操作

1) 插入结点运算        

在中序线索二叉树上插入结点可以分两种情况考虑:第一种情况是将新的结点插入到二叉树中,作某结点的左孩子;第二种情况是将新的结点插入到二叉树中,作某结点的右孩子。 下面我们仅讨论后一种情况。          

InsNode(BiThrNode *p, BiThrNode *r)表示在线索二叉树中插入r所指向的结点,做p所指结点的右孩子。此时有两种情况:

(1) 若结点p的右孩子为空,则插入结点r的过程很简单。        

原来p的后继变为r的后继        

结点p变为r的前驱        

结点r成为p的右孩子      

结点r的插入对p原来的后继结点没有任何的影响。

(2) 若p的右孩子不为空

p的右孩子变为r的右孩子结点

p变为r的前驱结点

r变为p的右孩子结点

这时还需要修改原来p的右子树中“最左下端”结点的左指针域,使它由原来的指向结点p变为指向结点r 

 若p的右孩子不为空,则插入后,p的右孩子变为r的右孩子结点, p变为r的前驱结点,r变为p的右孩子结点。这时还需要修改原来p的右子树中“最左下端”结点的左指针域,使它由原来的指向结点p变为指向结点r。

void InsNode(BiThrNode *p, BiThrNode *r)
{if(p->Rtag==1)    /* p无右孩子 */{r->RChild=p->RChild;   /* p的后继变为r的后继 */r->Rtag=1; p->RChild=r;    /* r成为p的右孩子 */r->LChild=p;   /* p变为r的前驱 */r->Ltag=1; 
}
Else     /* p有右孩子 */{s=p->RChild; while(s->Ltag==0)s=s->LChild;     /* 查找p结点的右子树的“最左下端”结点 */r->RChild=p->RChild;    /* p的右孩子变为r的右孩子 */r->Rtag=0;  r->LChild=p;    /* p变为r的前驱 */r->Ltag=1; p->RChild=r;     /* r变为p的右孩子 */s->LChild=r;     /* r变为p原来右子树的“最左下端”结点的前驱 */}
} 

将新结点r插入到中序线索二叉树中作结点p的左孩子的算法与上面的算法类似。

2)删除结点运算

与插入操作一样,在线索二叉树中删除一个结点也会破坏原来的线索,所以需要在删除的过程中保持二叉树的线索化。显然,删除操作与插入操作是一对互逆的过程。

例如,在中序线索二叉树中删除结点r的过程如图所示。 

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

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

相关文章

Alfred v5.1.4(mac快速启动)

Mac效率办公软件哪个好?Alfred是一款Mac电脑上的快速启动和工作流自动化工具,它可以帮助用户快速访问文件、应用程序、web搜索和系统工具,提高工作效率。以下是Alfred的特点: 快速启动:用户可以通过Alfred快速启动应用…

Node.js入门指南(四)

目录 express框架 express介绍 express使用 express路由 express 响应设置 中间件 路由模块化 EJS 模板引擎 express-generator hello,大家好!上一篇文章我们介绍了Node.js的模块化以及包管理工具等知识,这篇文章主要给大家分享Nod…

车载通信架构 —— 传统车内通信网络MOST总线(光纤传输、专精多媒体)

车载通信架构 —— 传统车内通信网络MOST总线(光纤传输、专精多媒体) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都…

(2023码蹄杯)省赛(初赛)第三场真题(原题)(题解+AC代码)

题目1&#xff1a;MC0227堆煤球 码题集OJ-堆煤球 (matiji.net) 思路&#xff1a; 1.i从l枚举到r,i是8的倍数就跳过&#xff0c;i不是8的倍数就用等差数列求和公式i(1i)/2,最后累加到答案中即可 AC_Code:C #include<bits/stdc.h> using namespace std;int main( ) {in…

轻松实现文件按数量平均分类,高效整理并自动新建文件夹保存“

你是否曾经因为文件数量过多&#xff0c;整理起来繁琐而感到烦恼&#xff1f;是否曾经为了新建文件夹而手动一个一个进行创建&#xff0c;费时又费力&#xff1f;现在&#xff0c;我们的智能文件管理工具将为你解决这些问题&#xff01; 首先第一步&#xff0c;我们要进入文件…

【开源】基于Vue.js的网上药店系统

项目编号&#xff1a; S 062 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S062&#xff0c;文末获取源码。} 项目编号&#xff1a;S062&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 药品类型模块2.3 药…

聊一聊索引覆盖的好处

问&#xff1a;索引覆盖啥意思&#xff1f; 答&#xff1a;若查询的字段在二级索引的叶子节点中&#xff0c;则可直接返回结果&#xff0c;无需回表。这种通过组合索引避免回表的优化技术也称为索引覆盖&#xff08;Covering Index&#xff09;。在叶子节点中的包括索引字段和主…

DataGrip 2023.2.3(IDE数据库开发)

DataGrip是一款数据库集成开发环境&#xff08;IDE&#xff09;&#xff0c;用于数据库管理和开发。 DataGrip提供了许多强大的功能&#xff0c;如SQL语句编辑、数据库连接管理、数据导入和导出、数据库比较和同步等等。它支持多种数据库&#xff0c;如MySQL、PostgreSQL、Ora…

Unity技美35——再URP管线环境下,配置post后期效果插件(post processing)

前两年在我的unity文章第10篇写过&#xff0c;后效滤镜的使用&#xff0c;那时候大部分项目用的还是unity的基础管线&#xff0c;stander管线。 但是现在随着unity的发展&#xff0c;大部分项目都用了URO管线&#xff0c;甚至很多PC端用的都是高效果的HDRP管线&#xff0c;这就…

位图及有关海量数据处理

bitset 1.给40亿个不重复的无符号整数&#xff0c;没排过序&#xff0c;给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个中 ①.如果用排序加二分查找&#xff0c;40亿个数需要16g内存&#xff0c;内存开不出这么大连续空间 ②.每个值映射一个比特位&#xff0c;…

基于Haclon的图形镜像案例

项目要求&#xff1a; 图为HALCON的例图“green-dot”&#xff0c;请将其中的圆形图案按水平和垂直两个方向分别进行镜像。 项目知识&#xff1a; 首先要用BLOB分析的方法&#xff0c;得到圆形图案的目标区域&#xff0c;再对其进行镜像。 在HALCON中与镜像相关的算子为mirr…

基于Eclipse+Swing+MySQL开发的借贷平台

基于Swing的借贷平台 项目介绍&#x1f481;&#x1f3fb; 本项目是一个基于Java JDBC的银行管理系统。开发环境为MyEclipse2014&#xff0c;数据库使用MySQL V5.5&#xff0c;操作系统为Windows 7 64位。 主要功能包括用户开户、存款、取款、转账、查询余额、修改密码和销户等…

两巨头Facebook 和 GitHub 联手推出 Atom-IDE

9月13日&#xff0c;GitHub 宣布与 Facebook 合作推出了 Atom-IDE —— 它包括一系列将类 IDE 功能带到 Atom 的可选工具包。初次发布的版本包括更智能、感知上下文的自动完成&#xff1b;导航功能&#xff0c;如大纲视图和定义跳转(outline view and goto-definition)&#xf…

生态对对碰|华为OceanStor闪存存储与OceanBase完成兼容性互认证!

近日&#xff0c;北京奥星贝斯科技有限公司 OceanBase 数据库与华为技术有限公司 OceanStor Dorado 全闪存存储系统、OceanStor 混合闪存存储系统完成兼容性互认证。 OceanBase 数据库挂载 OceanStor 闪存存储做为数据盘和日志盘&#xff0c;在 OceanStor 闪存存储系统卓越性能…

css优化滚动条样式

css代码&#xff1a; ::-webkit-scrollbar {width: 6px;height: 6px; }::-webkit-scrollbar-track {background-color: #f1f1f1; }::-webkit-scrollbar-thumb {background-color: #c0c0c0;border-radius: 3px; }最终样式&#xff1a;

TPLink-Wr702N 通过OpenWrt系统打造打印服务器实现无线打印

最近淘到了一个TPLink-Wr702N路由器&#xff0c;而且里面已经刷机为OpenWrt系统了&#xff0c;刚好家里有一台老的USB打印机&#xff0c;就想这通过路由器将打印机改为无线打印机&#xff0c;一番折腾后&#xff0c;居然成功了&#xff0c;这里记录下实现过程&#xff0c;为后面…

flink源码分析之功能组件(二)-kubeclient

简介 本系列是flink源码分析的第二个系列,上一个《flink源码分析之集群与资源》分析集群与资源,本系列分析功能组件,kubeclient,rpc,心跳,高可用,slotpool,rest,metrics,future。其中kubeclient上一个系列介绍过,为了系列完整性,这里“copy”一下。 kubeclient组件…

electron+vue3全家桶+vite项目搭建【26】electron本地安装Vue Devtool插件,安装浏览器扩展

文章目录 引入获取vue devtool导入插件排除插件的npm脚本最终效果 引入 demo项目地址 Vue Devtools插件是vue项目必备插件&#xff0c;它是安装在浏览器里的&#xff0c;而咱们的electron中实际就包含了一个浏览器&#xff0c;同理它也可以加载浏览器插件 获取vue devtool 直…

【开源】基于Vue+SpringBoot的食品生产管理系统

项目编号&#xff1a; S 044 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S044&#xff0c;文末获取源码。} 项目编号&#xff1a;S044&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3…

WIFI模块(esp-01s)获取网络时间与天气信息

目录 一、硬件连接 二、获取网络时间 1、AT指令集 2、具体操作 三、获取天气信息 1、心知天气注册 2、AT指令集 3、具体操作 4、json格式检查 一、硬件连接 WiFi模块的RX连接TTL模块的TX&#xff0c; WiFi模块的TX连接TTL模块的RX&#xff0c;电源与地接对。 插入电脑…