线索二叉树
为了解决无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题
但是同时出现了二叉链表找左、右孩子困难的问题,即在构建线索二叉树之后,链表的原来遍历方式会出问题。
--ltag为0表示lchild指向该节点的左孩子,为1表示lchild指向该节点的前驱
--rtag为0,表示rchild指向该节点的右孩子,为1时,rchild指向该节点的后继
#include <stdio.h>
#include <stdlib.h>
typedef char ElemType;
/*
线索存储标志位
Link(0):表示指向左右孩子的指针
Thread(1):表示指向前驱后继的线索
*/
typedef enum {Link, Thread} PointerTag;
typedef struct BiThrNode{
ElemType data;
struct BiThrNode *lchild, *rchild;
PointerTag ltag;
PointerTag rtag;
}BiThrNode, *BiThrTree;
//全局变量,始终指向刚刚访问过的节点
BiThrTree pre;
//创建一棵二叉树,约定用户遵照前序遍历方式输入数据
void createBiThrTree(BiThrTree *T){
char c;
scanf("%c", &c);
if('#' == c){
*T = NULL;
}else{
*T = (BiThrNode*)malloc(sizeof(BiThrNode));
(*T)->data = c;
(*T)->ltag = Link;
(*T)->rtag = Link;
createBiThrTree(&(*T)->lchild);
createBiThrTree(&(*T)->rchild);
}
}
//中序遍历线索化
void inThreading(BiThrTree T){
if(T){
inThreading(T->lchild);//递归左孩子线索华
//节点处理
if(!T->lchild){//处理前驱,什么时候能知道前驱,就是当前节点的时候,
//已经知道前一个是pre了,所以直接tag=thread,lchild=pre
T->ltag = Thread;
T->lchild = pre;
}
if(pre!=NULL && !pre->rchild){//处理后继,什么时候处理后继,只有访问到下一个的时候,
//才能知道下一个是谁,因为当访问下一个的时候,下一个是T,让pre的rchild指向T就好
pre->rtag = Thread;
pre->rchild = T;
}
pre = T;
inThreading(T->rchild);//递归右孩子线索华
}
}
//中序遍历线索二叉树---非递归
void InorderThreading(BiThrTree T){
BiThrTree p = T;
while(p != NULL){
//当ltag == Thread时,循环到中序序列第一个节点
while(p->ltag == Link){
p = p->lchild;
}
printf("%c ", p->data);
while(p->rtag == Thread && p->rchild != NULL){
p = p ->rchild;
printf("%c ", p->data);
}
p = p->rchild;
}
}
int main(){
BiThrTree T = NULL;
createBiThrTree(&T);
inThreading(T);
pre->rtag = Thread;
InOrderThreading(T);
return 0;
}
①前序输入构造二叉树
---输入c
---递归出口:如果c为#,那么表明该节点为空,*T = NULL;
---如果不等于#,表明该节点不为空
--------由于前序遍历,先根后左右,因此递归思想就是为当前节点分配空间,且左右tag都为link
--------然后递归左孩子
--------递归右孩子
②中序构造线索二叉树(参考中序遍历,如果T为空,递归出口,如果不为空,递归遍历当前节点左子树,然后打印出当前节点,递归遍历当前节点右子树,线索化只是把打印改成线索化操作)
---如果T为空,递归出口
---如果不为空,开始操作
------线索化当前T节点的左子树
------线索化是为了找到某个孩子为空的节点的前驱和后继,因此考虑什么时候能得到前驱,什么时候能得到后继
-----------处理前驱:针对当前节点,什么时候能得到前驱,访问当前节点的时候就可以,因此要记录上一个节点pre,不过前提是T的左子树为空;
-----------处理后继:针对pre节点,什么时候处理后继,访问下一个节点的时候,才能知道当前节点的后继是什么,因此对于下一个节点T来说,pre的后继就是T,因此要对pre进行操作,前提是pre!=NULL且pre的右子树为空
③中序遍历线索二叉树---非递归(左中右)
---得到根节点,将其给移动指针p
---如果p不为空,进行循环,因为线索化之后,相当于已经树给连接起来了,知道找到最后一个元素,那么他的地址就是空的
------循环到最左边的节点,如果ltag==link说明是真的有孩子,那么p=p->lchild
------输出该p节点的元素
------接下来后继,如果rtag为线索并且右子树不为空(担心右子树为空,还继续下去引起内存溢出),p=p->rchild;输出当前的p节点(用if也行,既然存在后继,即后继一定为某个父亲,而父亲只有两种情况,有没有右孩子,但不管有没有循环条件都不满足,即只执行一次)
------p=p->rchild’用来处理拥有右孩子的节点
相关阅读
origin: https://www.cnblogs.com/fantacity/p/3895771.html FAT 32 文件系统学习 1、本文的目标 本文将通过实际读取一个FAT3
一、写在前面鹅厂对产品经理的能力项要求中有一条重要考量,叫做技术理解力。我一直在思考学习,怎样才能算得上是具有技术理解力,也一
七月份我写的最后一篇文章,标题是:校园市场是个伪需求?这篇文章在seo实验室发布后,引起较大市场反响,而我的观点很清晰,一个4000亿的市
如何理解冲突域和广播域? 转载 冲突域: 【定义】在同一个冲突域中的每一个节点都能收到所有被发送的帧。简单的说就是同一时间内只
及时收集有用的用户反馈是一个长期的过程。当你已经清理了那些无用的反馈(例如超出现实的,纯粹假设的、来自第三方感受的),你仍然应