二叉树线索化C代码实现

有N个节点的链式结构的二叉树(lchild,data,rchild)一共用2N个指针域和N-1条分支线树(即已经占用的指针域),则还有2N-(N-1)=N+1个空指针域。

为了避免浪费资源,我们引入线索化。

线索化:将链式二叉树的空指针域利用起来,指向本节点的前驱或后继,形成线索。此种二叉树称为线索二叉树。

线索化的引入需要在节点中增加ltag和rtag标志域,根据其取值的不同判断*lchild和*rchild是指向的左右孩子,还是当前节点的前驱和后继。

二叉树线索化后如同链表一般,而当将遍历序列头和遍历序列尾分别与添加的头节点关联起来,则相当于一个双链表

遍历序列头、尾节点,头节点:

比如一个中序遍历序列 #B#D#A#C,B称为次遍历序列的头节点,C称为此遍历序列的尾节点,根节点是A,而头节点的左孩子指针域(或右)就指向根节点。

代码分别实现了:

StrAssign---生成静态先序遍历序列

CreateBiThrTree---利用先序遍历生成链式二叉树

InThreading-----中序遍历线索化的递归实现

InOrderThreading-------调用InThreading实现遍历序列头、尾与头节点的关联

InOrderTraverse_Thr------利用线索化二叉树链式结构 实现 非递归式中序遍历,大大降低了时间复杂度,时间复杂度为O(n)。

以上函数关系:CreateBiThrTree通过StrAssign得到先序遍历输入序列,生成链式二叉树,InOrderThreading通过中序遍历方式遍历二叉树,并构造头节点,通过线索化

生成一个类似双链表的链式二叉树,在此基础上,才可以通过InOrderTraverse_Thr实施非递归式的中序遍历。


难点解析:

InThreading---主要是递归,费脑力,这里将讲解InOrderThreading中的InThreading

在本例中,树的递归实现是从根节点开始,根节点就代表一颗树。以最简单的三个节点的满二叉树为例 BAC(中序遍历,A是根节点),D为头节点。

以为下分别用A,B,C表示指向三个节点的指针,并在三节点的数据域中写入A,B,C分别代表三个节点。

图中没有ltag和rtag值的变化,只是对指针指向做个解析。


递归解析:

初始状态如图1所示。



1.从根节点开始 InThreading(A),进入函数到InThreading(A->lchild)=InThreading(B)

2.进入InThreading(B),再到InThreading(B->lchild)

3.进入InThreading(B->lchild),由于B->lchild为空,则退回到InThreading(B)中

4.在InThreading(B)中,此时的pre=D,由于B->lchild为空,则执行

if(!B->lchild)                //如果没有左孩子
{
B->ltag = Thread;     //前驱线索化
B->lchild = pre;      //左孩子指针指向前驱
}
if(!pre->rchild)
{
pre->rtag = Thread;   //后继线索
pre->rchild = B;      //前驱右孩子指针指向后继节点(当前节点为(*T))
}
                pre = B;                      //保持pre指向(*T)的前驱
InThreading(&B->rchild);

执行第4步完毕的状态如图2所示。



5.在InThreading(B)中,执行InThreading(&B->rchild),B->rchild为空,退回到InThreading(B)中,InThreading(B)执行完,

    即InThreading(A->lchild)执行完,退回到InThreading(A)中。


6.在InThreading(A)中,此时的pre=B,由于A存在左孩子,则执行

if(!pre->rchild)                //前驱没有右孩子
{
pre->rtag=Thread;       //后继线索 
pre->rchild=A;          //前驱右孩子指针指向后继(当前结点A)
}
pre=A;                          //保持pre指向p的前驱 
InThreading(A->rchild);         //递归右子树线索化

执行第6步完毕后的状态图3所示。




7.执行6后,pre=A,并进入InThreading(A->rchild),即InThreading(C)

8.在InThreading(C)中,执行InThreading(C->rchild),由于C->rchild为空,则回到InThreading(C)中。

9.在InThreading(C)中,此时pre=A,且pre->rchild不为空,则执行

 if(!C->lchild)                //如果没有左孩子
{
C->ltag = Thread;      //前驱线索化
C->lchild = pre;       //左孩子指针指向前驱
}
                pre = C;                      //保持pre指向(*T)的前驱
InThreading(&C->rchild);

10.在InThreading(C)中,一直执行到InThreading(&C->rchild),由于C->rchild为空,  从InThreading(&C->rchild)返回到InThreading(C)中,并且InThreading(C)也执行完,

  返回到InThreading(A)中,InThreading(A)执行完,此时的pre=C,即遍历序列的尾节点。

  递归完毕后如图4所示。



以下是完整的代码实现:(站在巨人的肩膀上)

运行平台:CenOS6.4   GCC 4.4.7

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100

typedef int Status;
typedef char ElemType;
ElemType Nil = '#';

typedef enum {Link,Thread} PointerTag; //Link=0表示指向左右孩子指针
                                       //Thread=1表示指向前驱动或后继的线索
									   
typedef struct BiThrNode
{
	ElemType data;
	struct BiThrNode *lchild,*rchild;
	PointerTag ltag;
	PointerTag rtag;
}BiThrNode,*BiThrTree;

typedef ElemType String[MAXSIZE];
String StrT;
/*辅助函数:产生静态输入序列*/
Status StrAssign(String StrT,char *chars)
{
	if(NULL == chars||strlen(chars)>MAXSIZE)
	{
		return ERROR;
	}
	int i;
	for(i=0;i<strlen(chars);i++)//在for()中定义i,则在编译时添加参数 -std=c99,而在此种
								//模式下,CreateBiThrTree中exit(OVERFLOW)会出现OVERFLOW
								//undeclare 错误
	{
		StrT[i] = *(chars+i);
	}
	return OK;
}
/*end*/

int pos = 0; //StrT的位置索引

/*利用前序遍历递归的方式生成二叉树*/
Status CreateBiThrTree(BiThrTree *T)
{
	if(NULL==T)
	{
		return ERROR;
	}
	ElemType ch;
	ch = StrT[pos++];
	if(Nil == ch)
	{
		*T = NULL;
	}else
	{
		*T = (BiThrTree)malloc(sizeof(BiThrNode));
		if(NULL == *T)
		{
			exit(OVERFLOW);//
		}
		(*T)->data = ch;
		CreateBiThrTree(&(*T)->lchild);
		if((*T)->lchild)
		{
			(*T)->ltag = Link;
		}
		CreateBiThrTree(&(*T)->rchild);
		if((*T)->rchild)
		{
			(*T)->rtag = Link; //这里写成了Thread,导致调试了很久
		}
		return OK;
	}
}
/*end*/

/*中序遍历线索化*/
BiThrTree pre=NULL;//始终指向访问过的节点的全局变量

Status InThreading(BiThrTree *T)
{
	if(NULL == *T)
	{
		return ERROR;
	}
	if(T)
	{
		InThreading(&(*T)->lchild);/*递归左子树线索化*/
		if(!(*T)->lchild)         //如果没有左孩子
		{
			(*T)->ltag = Thread;  //前驱线索化
			(*T)->lchild = pre;   //左孩子指针指向前驱
		}
		if(!pre->rchild)
		{
			pre->rtag = Thread;   //后继线索
			pre->rchild = *T;     //前驱右孩子指针指向后继节点(当前节点为(*T))
		}
		pre = (*T);               //保持pre指向(*T)的前驱
		InThreading(&(*T)->rchild);  /*递归右子树线索化*/
	}
}
/*end*/

/*构建首尾相连的线索化二叉树链式结构*/
/*3步:(1)创建头节点
 *     (2)线索化
 *     (3)指针互联*/ 

Status InOrderThreading(BiThrTree *head,BiThrTree *T)
{
	*head = (BiThrTree)malloc(sizeof(BiThrNode));
	if(!*head)
	{
		exit(OVERFLOW);
	}
	/*构建头节点*/
	(*head)->ltag = Link; 
	(*head)->rtag = Thread;
	(*head)->rchild = (*head);
	if(!(*T))
	{
		(*head)->lchild = (*head);
	}
	/*构建完毕*/
	else
	/*线索化,指针互联*/
	{
		(*head)->lchild = (*T);
		pre = (*head);
		InThreading(T);//线索化后,pre已经被赋值为 遍历序列末尾节点的指针
		pre->rtag = Thread;
		pre->rchild = (*head); 
		(*head)->rchild = pre;
		return OK;
	}
}
/*end*/ 

/*中序遍历 线索二叉树T--非递归算法*/
Status InOrderTraverse_Thr(BiThrTree *T)
{
	if(NULL == T)
	{
		return ERROR;
	}
	BiThrTree p;
	p = (*T)->lchild;
	while(p!=(*T))
	{
	    /*访问左子树*/
		while(p->ltag==Link)
		{
			p = p->lchild;
		}
		printf("%c ",p->data);
		/*访问右子树*/
		while(p->rtag==Thread&&p->rchild!=(*T))
		{
			p = p->rchild;
			printf("%c ",p->data);
		}
		p=p->rchild;
	}
}
/*end*/

int main()
{
    char *chars = "ABDH#K###E##CFI###G#J##";
	StrAssign(StrT,chars);
    BiThrTree *T,*head;
	T = (BiThrTree *)malloc(sizeof(BiThrTree));
	head = (BiThrTree *)malloc(sizeof(BiThrTree));
	*T = NULL;
	*H = NULL;
    CreateBiThrTree(T);
    /*中序遍历,并线索化二叉树*/
	InOrderThreading(head,T);
	printf("非递归中序遍历\n");
	InOrderTraverse_Thr(H);
	printf("\n");
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值