数据结构之线索二叉树的基本概念和构造

数据结构之线索二叉树的基本概念和构造

1.基本概念

二叉树的非递归遍历算法避免了系统栈的调用,提高了一定的效率,而线索二叉树可以把用户栈也省掉,把二叉树的遍历过程线性化,从而进一步提高效率。

对于二叉链表存储结构,n个结点的二叉树就有n+1个空链域,能不能把这些空链域有效地利用起来使得二叉树的遍历更加高效呢?答案是肯定的,这也是线索二叉树的由来。在一般的二叉树中,我们只知道某个结点的左右孩子,并不能知道某个结点在某种遍历方式下的直接前驱和直接后继,如果能够知道前驱和后继的信息,就可以把二叉树看作一个链表结构,从而可以像遍历链表那样来遍历二叉树,进而提高效率。

线索二叉树可以分为前序线索二叉树、中序线索二叉树和后序线索二叉树。对一棵二叉树中的所有结点的空指针域按照某种遍历方式加线索的过程称为线索化,被线索化了的二叉树称为线索二叉树。

2.中序线索二叉树的构造

typedef struct TBTNode
{
    
    
	char data;
	int ltag,rtag;
	struct TBTNode* lchild;
	struct TBTNode* rchild;
}

在二叉树线索化的过程中会把树中的空指针利用起来作为寻找当前结点前驱或后继的线索,这样就出现了一个问题,即线索和树中原有指向孩子结点的指针无法区分。

ltag和rtag就是为了区分这两类指针,它们为标志域,具体意义如下:

  • 若ltag=0,则表示lchild为指针,指向结点的左孩子;若ltag=1,则表示lchild为线索,指向结点的直接前驱。
  • 若rtag=0,则表示rchild为指针,指向结点的右孩子;若rtag=1,则表示rchild为线索,指向结点的直接后继。

2.1二叉树中序线索化分析

既然要对二叉树进行中序线索化,首先要有个中序遍历的框架,这里采用二叉树中序递归遍历算法,在遍历过程中连接上合适的线索即可。

线索化的规则是,左线索指针指向当前结点在中序遍历序列中的前驱结点,右线索指针指向后继结点。因此我们需要一个指针p指向当前正在访问的结点,pre 指向p的前驱结点,p的左线索如果存在则让其指向pre, pre 的右线索如果存在则让其指向p,因为p是pre的后继结点,这样就完成了一对线索的连接。

上一步中保持pre始终指向p前驱的具体过程是,当p将要离开一个访问过的结点时,pre指向p,当p来到一个新结点时,pre显然指向的是此时p所指结点的前驱结点。

通过中序遍历对二叉树线索化的递归算法如下:

扫描二维码关注公众号,回复: 12436417 查看本文章
void InThread (TBTNode *p, TBTNode *&pre)
{
    
    
	if (p!=NULL)
	{
    
    
		InThread(p->lchild,pre) ;//递归,左子树线索化
		if (p->1child==NULL)
		{
    
    
			//建立当前结点的前驱线索
			p->lchild=pre;
			p->ltag=1;
		}
		if (pre!=NULL&&pre->rchild==NULL)
		{
    
      
			//建立前驱结点的后继线索
			pre-> rchild=P;
			pre->rtag=1;
		}
		
		
		pre=p;//pre指向当前的p,作为p将要指向的下一个结点的前驱结点指示指针	
		p=p->rchild;//p指向一个新结点,此时pre和p分别指向的结点形成了一个前
					//驱后继对,为下一次线索的链接做准备
		InThread(p,pre);//递归,右子树线索化
	}
}

则通过中序遍历建中序线索二义树的主程序如下:

void createInThread (TBTNode  *root)
{
    
    
	TBTNode* pre=NUlL;
	if(root!=NULL)
	{
    
    
		//前驱结点指针
		InThread (root,pre);
		pre->rchild=NULL;//非空二叉树,线索化
		pre->rtag=1; //后处理中序最后一个结点
	}
}

猜你喜欢

转载自blog.csdn.net/PecoHe/article/details/101984025