为啥需要线索二叉树
我想正如程序猿发觉单链表并不总能满足他们设计的程序某些要求的时候,发明了双向链表来弥补一样,线索二叉树也是在需求中被创造的! 那普通的二叉树到底有什么缺陷让我们发指呢? 一,浪费空间 二,浪费时间
哪种遍历方式更适合节省"^"所浪费的空间
如上图(可以节省空间的节点有红色表示)
前序:ABDHIECFG
中序:HDIBEAFCG
后序:HIDEBFGCA
事实证明中序遍历更节省“^”所浪费的空间。
结构展示
代码实现
成果图展示
主要思路:
1、声明一个BiTree t表示当前节点(T)的前驱。
2、若T无左孩子则将标志位置为Thread将T的左孩子指向t。
3、若前驱节点(t)无右孩子则将标志位置为Thread,将t的右孩子指向T。
#include <stdio.h>
#include <stdlib.h>
//线索二叉树
//ABDH##i##EJ###CF##G##
//ABC##D##E#F##
typedef char Elemtype;
//线索存储标志位
//Link(0) 表示左右儿子的指针
//Thread(1)表示指向前驱后继的线索
typedef enum {Link,Thread} point;
typedef struct BiThread{
Elemtype data;//数据域
struct BiThread *lchild,*rchild;//指针域
point ltag,rtag;//标志位 Link(0) 表示左右儿子的指针 Thread(1)表示指向前驱后继的线索
}BiThread,*BiTree;
//全局变量始终指向刚刚访问过的节点
BiTree t;
//为了递推中序遍历的方便设置一个全局变量指向头节点(注意不是根节点)
BiTree p;
//按前序遍历创建二叉树
void create(BiTree *T){
char c;
c = getchar();
if(c != '#'){
(*T) = (BiTree)malloc(sizeof(BiThread));
(*T)->data = c;
(*T)->ltag = Link;
(*T)->rtag = Link;
create(&(*T)->lchild);
create(&(*T)->rchild);
}else{
(*T) = NULL;
}
}
//中序遍历线索化
void xiansuo(BiTree *T){
if((*T) != NULL){
//递归左孩子线索化
xiansuo(&(*T)->lchild);
//后继节点的处理(如果该节点没有左孩子,设置ltag = Thread,并且把lchild指向前驱节点)
if((*T)->lchild == NULL){
(*T)->ltag = Thread;
(*T)->lchild = t;
}
//前驱节点的处理(如果前驱节点没有右孩子,设置rtag = Thread,并且把rchild指向当前节点)
if(!t->rchild){
t->rtag = Thread;
t->rchild = (*T);
}
t = (*T);
//递归右孩子线索化
xiansuo(&(*T)->rchild);
}
}
//在线索化前和后的工作
void zhunbei(BiTree T){
//为头节点开辟空间
t = (BiTree)malloc(sizeof(BiThread));
//p指针指向头节点
p = t;
//设置头节点左右标志位为线索Thread
p->ltag = Thread;
p->rtag = Thread;
//若为空树则左指针指向自己 右指针也指向自己
if( !T ){
p->lchild = p;
p->rchild = p;
}else{
//若非空树 则左指针指向根节点 右指针指向中序遍历的最后一个节点 最后一个节点右指针指向头节点
p->lchild = T;
xiansuo(&T);
p->rchild = t;
t->rchild = p;
t->rtag = Thread;
}
}
//递归的中序遍历
void show(BiTree T){
if(T != NULL){
if(T->lchild == Link){
show(T->lchild);
}
printf("%c",T->data);
if(T->rchild == Link){
show(T->rchild);
}
}
}
//非递归的中序遍历
void show1(BiTree T){
//声明变量q指针指向根节点
BiTree q = p->lchild;
// p==q 2种情况
//1-T为空树
//2-线索二叉树遍历完成
while(p != q){
//寻找q->ltag == Link最左边的子树 也就是中序遍历开始的起点
while(q->ltag == Link){
q = q->lchild;
}
//打印出该子树
printf("%c",q->data);
//为啥为rtag == Thread 因为中序遍历的顺序和右标志位为Thread一致 (也就是一直找线索化的后继节点)
while(q->rtag == Thread && q->rchild != p){
q = q->rchild;
printf("%c",q->data);
}
//若q->rtag != Thread 往下一个右节点遍历
q = q->rchild;
}
}
int main()
{
BiTree x;
create(&x);
zhunbei(x);
show1(x);
return 0;
}