线索二叉树

萝らか妹 提交于 2020-02-20 05:50:19

1.产生背景

现有一棵结点数目为n的二叉树,采用二叉链表的形式存储。对于每个结点均有指向左右孩子的两个指针域,而结点为n的二叉树一共有n-1条有效分支路径。那么,则二叉链表中存在2n-(n-1)=n+1个空指针域。那么,这些空指针造成了空间浪费。
例如:图1所示一棵二叉树一共有10个结点,空指针^有11个。
图1

此外,当对二叉树进行中序遍历时可以得到二叉树的中序序列。例如:图2.1所示二叉树的中序遍历结果为HDIBJEAFCG,可以得知A的前驱结点为E,后继结点为F。但是,这种关系的获得是建立在完成遍历后得到的,那么可不可以在建立二叉树时就记录下前驱后继的关系呢,那么在后续寻找前驱结点和后继结点时将大大提升效率。

2.线索化

现将某结点的空指针域指向该结点的前驱后继,定义规则如下:

若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
若结点的右子树为空,则该结点的右孩子指针指向其后继结点

这种指向前驱和后继的指针称为线索。将一棵普通二叉树以某种次序遍历,并添加线索的过程称为线索化。
按照规则将图1所示二叉树线索化后如图2所示
图2
图中黑色点画线为指向后继的线索,紫色虚线为指向前驱的线索。
可以看出通过线索化,既解决了空间浪费问题,又解决了前驱后继的记录问题。

3.线索化带来新问题

经过上一节讲解后,可以将一棵二叉树线索化为一棵线索二叉树,那么新的问题产生了。我们如何区分一个结点的lchild指针是指向左孩子还是前驱结点呢?例如:对于图2所示的结点E,如何区分其lchild的指向的结点J是其左孩子还是前驱结点呢?
为了解决这一问题,现需要添加标志位ltag,rtag。并定义规则如下:

ltag为0时,指向左孩子,为1时指向前驱
rtag为0时,指向右孩子,为1时指向后继

添加ltag和rtag属性后的结点结构如下
在这里插入图片描述
图2所示线索二叉树转变为图3所示的二叉树。
在这里插入图片描述
内容转自:https://www.jianshu.com/p/3965a6e424f5

4.代码实现

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

typedef char ElemType;

// 线索存储标志位
// Link(0):表示指向左右孩子的指针
// Thread(1):表示指向前驱后继的线索
typedef enum {Link, Thread} PointerTag;

typedef struct BiThrNode
{
	char data;
	struct BiThrNode *lchild, *rchild;
	PointerTag ltag;
	PointerTag rtag;
} BiThrNode, *BiThrTree;

// 全局变量,始终指向刚刚访问过的结点
BiThrTree pre;

// 创建一棵二叉树,约定用户遵照前序遍历的方式输入数据,无孩子用空格代替
void CreateBiThrTree( BiThrTree *T )
{
	char c;

	scanf("%c", &c);
	if( ' ' == c )
	{
		*T = NULL;
	}
	else
	{
		*T = (BiThrNode *)malloc(sizeof(BiThrNode));
		(*T)->data = c;
		(*T)->ltag = Link;
		(*T)->rtag = Link;

		CreateBiThrTree(&(*T)->lchild);
		CreateBiThrTree(&(*T)->rchild);
	}
}

// 中序遍历线索化
void InThreading(BiThrTree T)
{
	if( T )
	{
		InThreading( T->lchild );		// 递归左孩子线索化

		if( !T->lchild )	// 如果该结点没有左孩子,设置ltag为Thread,并把lchild指向刚刚访问的结点。
		{
			T->ltag = Thread;
			T->lchild = pre;
		}

		if( !pre->rchild ) //如果前一个结点没有右孩子,将其指向当前结点
		{
			pre->rtag = Thread;
			pre->rchild = T;
		}

		pre = T;

		InThreading( T->rchild );		// 递归右孩子线索化
	}
}

//p :头结点  , T :原二叉树根结点
void InOrderThreading( BiThrTree *p, BiThrTree T )
{
	//初始化头结点
	*p = (BiThrTree)malloc(sizeof(BiThrNode));
	(*p)->ltag = Link;
	(*p)->rtag = Thread;
	(*p)->rchild = *p;
	
	
	if( !T )//如果树为空
	{
		(*p)->lchild = *p;
	}
	else
	{
		(*p)->lchild = T;//树不为空的话,头结点左孩子指向原二叉树根结点
		pre = *p;
		
		InThreading(T);
		
		//遍历完树后,将遍历时的最后一个结点的右孩子指向头结点,
		//并且,将头结点右孩子指向遍历时的最后一个结点
		pre->rchild = *p;
		pre->rtag = Thread;
		(*p)->rchild = pre;
	}
}


int main()
{
	BiThrTree P, T = NULL;

	CreateBiThrTree( &T );

	InOrderThreading( &P, T );


	return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!