[数据结构]第六章-树和二叉树(读书笔记3)

线索二叉树
二叉树遍历的缺点:当以二叉树链表作为存储结构时,只能找到结点的左右孩子信息,而不能直接得到结点在任一序列中的前驱和后继信息,这种信息只有在遍历的动态过程中才能得到。
如何保存这种在遍历过程中得到的信息呢?一个最简单的办法是在每个结点上增加两个指针域fwd和bkwd,分别指示结点在任一次序遍历时得到的前驱和后继信息。
二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索。加上线索的二叉树称之为线索二叉树(Threaded Binary Tree)。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。
线索二叉树通过设置LTAG,RTAG标志,并事先线索化来提高整个二叉树的遍历速度,是个典型的牺牲空间来提升速度的例子。线索二叉树方便查找一个结点在访问次序上的前后邻居。
LTag = 0 lchild域指示结点的左孩子
LTag = 1 lchild域指示结点的前驱
RTag = 0 lchild域指示结点的右孩子
RTag = 1 lchild域指示结点的后继
在线索树上进行遍历,只要先找到序列中结点,然后依次找结点后继直至其后继为空时为止。
根据中序遍历的规律可知,结点的后继应是遍历其右子树时访问的第一个结点。即右子树中最左下的结点。在中序线索二叉树上遍历二叉树,虽则时间复杂度亦为O(n),但常数因子要小很多,且不需要设栈。因此,若在某程序中所用二叉树需经常遍历或查找结点在遍历所得线性序列中的前驱和后继,则应采用线索链表作存储结构。
由于线索化的实质是将二叉链表中的空指针改为指向前驱或后继的线索,而前驱或后继的信息只有在遍历时才能得到,因此线索化的过程即为在遍历的过程中修改空指针的过程。

/*Link == 0 指针 Thread == 1 线索*/
typedef enum PointTag {
	Link,
	Thread
}PointerTag;
typedef struct BiThrNode{
	TElemType data;
	struct BiThrNode *lchild, *rchild;/*左右孩子指针*/
	PointerTag	LTag,RTag;/*左右标志*/
}BiThrNode,*BiThrTree;

Status CreateBiThrTree(BiThrTree *T)
{ /* 按先序输入二叉线索树中结点的值,构造二叉线索树T */
/* 0(整型)/空格(字符型)表示空结点 */
	TElemType h;
#if CHAR
	scanf("%c",&h);
#else
	scanf("%d",&h);
#endif
	if(h == NIL){
		*T=NULL;
	}
	else{
		*T=(BiThrTree)malloc(sizeof(BiThrNode));
		if(!*T){
			exit(OVERFLOW);
		}
		(*T)->data = h; /* 生成根结点(先序) */
		CreateBiThrTree(&(*T)->lchild); /* 递归构造左子树 */
		if((*T)->lchild){ /* 有左孩子 */
			(*T)->LTag = Link;
		}
		CreateBiThrTree(&(*T)->rchild); /* 递归构造右子树 */
		if((*T)->rchild){ /* 有右孩子 */
			(*T)->RTag = Link;
		}
	}
	return OK;
}

BiThrTree pre; /* 全局变量,始终指向刚刚访问过的结点 */
void InThreading(BiThrTree p)
{
	if (p){
		/* 中序遍历进行中序线索化。算法6.7 */
		InThreading(p->lchild);	//左子树线索化
		if(!p->lchild){//前驱线索
			p->LTag = Thread;
			p->lchild = pre;
		}
		if(!pre->rchild){//后继线索
			pre->RTag = Thread;
			pre->rchild = p;
		}
		pre = p; /* 保持pre指向p的前驱 */
		InThreading(p->rchild);	//右子树线索化
	}
}
Status InOrderThreading(BiThrTree Thrt, BiThrTree T)
{//中序遍历二叉树T,并将其中序线索化,Thrt指向头结点
	if(!(Thrt = (BiThrTree)malloc(sizeof(BiThrTree)) )){
		exit(OVERFLOW);
	}
	//建头结点
	Thrt->LTag = Link;
	Thrt->RTag = Thread;
	//右指针回指
	Thrt->rchild = Thrt;
	//若二叉树空,则左指针回指
	if (T == NULL){
		Thrt->lchild = Thrt;
	}
	else{
		Thrt->lchild = T;
		pre = Thrt;
		InThreading(T);
		//最后一个结点线索化
		pre->rchild = Thrt;
		pre->RTag = Thread;
		Thrt->rchild = pre;
	}
	return OK;
}
Status InOrderTraverse_Thr(BiThrTree T,Status(*Visit)(TElemType))
{	/* 中序遍历二叉线索树T(头结点)的非递归算法。算法6.5 */
	BiThrTree p;
	//p指向根结点
	p = T->lchild;
	while(p != T){//空树或遍历结束时,p==T
		while(p->LTag == Link){
			p = p->lchild;
		}
		if(!Visit(p->data)){
			return ERROR;
		}
		while (p->RTag == Thread && p->rchild != T){
			p = p->rchild;
			Visit(p->data);//访问后继结点
		}
		p = p->rchild;
	}
	return OK;
}
Status vi(TElemType c)
{
#if CHAR
	printf("%c ",c);
#else
	printf("%d ",c);
#endif
	return OK;
}
int _tmain(int argc, _TCHAR* argv[])
{ 
	BiThrTree H = NULL,T;
#if CHAR
	printf("请按先序输入二叉树(如:ab三个空格表示a为根结点,b为左子树的二叉树)\n");
#else
	printf("请按先序输入二叉树(如:1 2 0 0 0表示1为根结点,2为左子树的二叉树)\n");
#endif
	CreateBiThrTree(&T); /* 按先序产生二叉树 */
	InOrderThreading(H,T); /* 中序遍历,并中序线索化二叉树 */ 
	printf("中序遍历(输出)二叉线索树:\n");
	InOrderTraverse_Thr(T,vi); /* 中序遍历(输出)二叉线索树 */
	printf("\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值