数据结构学习笔记-线索二叉树

线索二叉树

二叉树遍历的实质:以一定的规则将一个非线性结构进行线性化操作,使每个结点在这些线性序列中有且仅有一个直接前驱和直接后继。但是,当以二叉链表为存储结构时只能在遍历的过程中才能得到结点的前驱、后继,为了存储这种信息。引入线索二叉树。

根据二叉树的性质有:n0=n2+1,空链域:2n0+n1(也就是叶子结点有2个空链域,度为1的结点有1个空链域)另外有n=n0+n1+n2因此,有n个结点组成的二叉树中,有n+1个指针是空指针。二叉树的线索化就是将这些空指针进行修改的过程。

线索二叉树的结点中分别添加了LTag和RTag标志域

结点的结构为lchild、LTag、data、RTag、rchild,以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化

线索二叉树的产生

构造线索二叉树和构造普通的二叉树方法相似,区别有两点:

1>结点结构不同

2>构造线索二叉树时,若有左右孩子结点,还要给左右标志赋值0(Link)

需要注意的是对一棵给定的二叉树,其先序、中序、后序和层序遍历的顺序是不同的。显然,其线索化的操作和遍历的操作也是不同的。下面以中序线索二叉树为例进行说明。

中序线索二叉树的存储结构和普通二叉树的存储结构,区别有三点:

1>线索二叉树多了一个头结点。其左孩子指针指向根结点,右孩子指针指向中序遍历的最后一个结点。

2>它每个结点的左右孩子指针都不是空指针。在没有孩子的情况下分别指向该结点的前驱和后继

3>中序遍历的第1个结点的左孩子指针(线索)和最后1个结点的右孩子指针(线索)都指向头结点。其目的是标志遍历的起点和终点。

生成中序线索二叉树分两步:

1>产生线索链表

2>将链表线索化

首先,给出产生线索链表的代码,以及用到的结构体

typedef enum PointerTag{Link,Thread};

typedef struct Bttnode  //binary thread tree
{
	char data;
	struct Bttnode *lchild;
	struct Bttnode *rchild;
	PointerTag RTag,LTag;
}BTTNODE;

BTTNODE *creat_binary_tree(BTTNODE *root)
{
	char x;
	scanf("%c",&x);
	if(x!=' ')
	{
		root = (BTTNODE *)malloc(sizeof(BTTNODE));
		if(NULL==root)
		{
			printf("memory distribution error\n");
			exit(-1);
		}
		root->data = x;
		root->lchild = creat_binary_tree(root->lchild);
		if(root->lchild)
			root->LTag = Link;
		root->rchild = creat_binary_tree(root->rchild);
		if(root->rchild)
			root->RTag = Link;
		return root;
	}
	else
		return NULL;
}
//先建立具有BTTNODE类型结点的二叉树,随后对其进行二叉树的线索化
生成线索链表的方法跟生成普通的二叉树很类似,区别在于结点不同,以及多了两条用来设定LTag和RTag的if语句。
其次,给出链表的中序线索化
BTTNODE *InOrderThreading(BTTNODE *root)
{
	BTTNODE *Thrt,*p;
	p = root;//p表示指向的当前结点
	Thrt = (BTTNODE *)malloc(sizeof(BTTNODE));
	if(NULL==Thrt)
	{
		printf("memory distribution error\n");
		exit(-1);
	}
	Thrt->LTag = Link;
	Thrt->RTag = Thread;
	Thrt->rchild = Thrt;//先指向自己,最后再指向中序遍历时最后访问的结点
	if(!root)
	{
		Thrt->lchild = Thrt;
	}
	else
	{
		Thrt->lchild = p;
		pre = Thrt;    //pre初值是头结点,不是根结点!
		InThreading(root);
		pre->rchild = Thrt;
		pre->RTag = Thread;
		Thrt->rchild = pre;
	}
	return Thrt;
}

void InThreading(BTTNODE *p)
{
	if(p)
	{
		InThreading(p->lchild);
		if(!p->lchild)
		{
			p->LTag = Thread;
			p->lchild = pre;
		}
		if(!pre->rchild)
		{
			pre->RTag = Thread;
			pre->rchild = p;
		}
		pre = p;
		InThreading(p->rchild);
	}
}
线索化中引入了一个头结点,该头结点的左孩子指向根结点,右孩子指向中序遍历的最后一个结点,因此LTag=Link,RTag=Thread。另外,中序遍历的第一个结点的前驱指向头结点(即该结点的lchild域指向头结点),遍历的最后一个结点的后继也指向头结点(即该结点的rchild域指也向头结点)。目的是:即可从第一个结点起顺后继进行遍历,也可从最后一个结点起顺前驱进行遍历。
线索二叉树的中序遍历(这里是从第一个结点起顺后继遍历):
void InOrderTraverse_Thr(BTTNODE *Thrt)
{
	BTTNODE *p;
	p = Thrt->lchild;
	while(p!=Thrt)                     //当二叉树为空,或者遍历结束
	{
		while(p->LTag==Link)
		{
			p = p->lchild;         //沿着左子树一路向下
		}
		printf("%c",p->data);
		while(p->RTag==Thread&&p->rchild!=Thrt)//一开始写成了if语句,产生的问题就是当输入为bad  e  cf  g  时,程序一直输出dadadada...
		{
			p = p->rchild;
			printf("%c",p->data);
		}
			p = p->rchild;
	}
}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值