线索二叉树

线索二叉树的操作


线索二叉树的存储结构

二叉树的存储结构

typedef struct BiTNode
{
	char data;
	struct BiTNode* lchild,*rchild;
}BiTNode,*BiTree;

结点由存放数据元素的数据域和存放放左右孩子结点地址的指针域组成

线索二叉树的存储结构

typedef struct BiThrNode
{
	char data;
	struct BiThrNode *lchild, *rchild;
	int LTag;
	int RTag;
}BiThrNode,*BiThrTree;

相较于二叉树添加了LTag和RTag标记
当标记值为0时代表该结点有儿子结点,标记值为1时其lchild指针指向上一个结点,rchild指针指向下一个结点。

建立线索二叉树的目的是最大化利用空指针,构建二叉树中许多结点并没有左右儿子结点,因此他们的lchildrchild指针均没有被利用。
我们将没有利用的指针指向该结点的前驱或后继成为线索,对二叉树以某种次序遍历使其变为线索二叉树的过程称作线索化,


一、先序遍历建立二叉链表

目的:以先序遍历的方法建立二叉链表
实现:从键盘输入,检测到“#”即设置指针为空,否则分配内存空间建立新结点,以先序遍历的方法递归建立二叉链表

void PreCreateBiTree(BiTree &T)						
{
	char ch;
	cin>>ch;
	if (ch=='#')
	{
		T=NULL;
	}
	else
	{
		T=new BiTNode;
		if(!T) cout<<"error!"<<endl;
		(T)->data=ch;
		PreCreateBiTree((T)->lchild);
		PreCreateBiTree((T)->rchild);
	}
}

通过先序遍历建立二叉树要求我们将没有数据的结点用“#”代替输入,输入的字符串应该是对于拼凑后的二叉树的序列

线索二叉树

“拼凑”后的完全二叉树如图
此二叉树的先序遍历为“AB#D##C##”


二、二叉树的中序线索化

目的:以中序遍历的方法使二叉链表线索化
实现:
①pre是全局变量,始终指向刚刚问过的结点(当前结点的前一个结点)
②从树根开始,先遍历到最深的左孩子结点,如果该结点没有左孩子,将LTag置为1做标记,令其lchild指针指向pre前驱,否则将LTag置为0,其lchild指针仍然指向左孩子结点。
③如果前驱没有右孩子结点,将前驱的RTag置为1做标记,令其rchild指针指向当前结点p,保持pre=p继续遍历,接着遍历右子树。

BiThrTree pre;
void InThreading(BiThrTree p)
{
	if (p)
	{
		InThreading(p->lchild);
		if (!p->lchild)
		{
			p->LTag=1;
			p->lchild=pre;
		}
		else p->LTag=0;
		if (!pre->rchild)
		{
			pre->RTag=1;
			pre->rchild=p;
		}
		else pre->RTag=0;
		pre=p;
		InThreading(p->rchild);
	}
}

线索二叉树

B为遍历的第一个结点,当时pre为全局变量,自动初始化为NULL,故B的lchild指针指向null


三、带头结点的二叉树中序线索化

功能:为二叉树添加头结点后中序线索化
实现:
①创建头结点,申请内存空间,将LTag置为0,左孩子结点指向二叉树的根结点。将RTag置为1,初始化时默认右孩子指针指向自己,如果树为空,左孩子指针也指向自己。
②若树非空则开始中序线索化,将左孩子指向二叉树根结点,pre前置指针指向头结点,开始中序线索化。
③中序线索化结束,pre指针指向中序遍历最后一个结点,将最后一个结点的右孩子指针指向头结点,标记RTag为1。
④最后让头结点的右孩子指针指向最后一个结点,连成双向的二叉链表。

void InOrderThreading(BiThrTree &Thrt,BiThrTree T)
{
	Thrt=new BiThrNode;
	Thrt->LTag=0;
	Thrt->RTag=1;
	Thrt->rchild=Thrt;
	if(!T) Thrt->lchild=Thrt;
	else
	{
		Thrt->lchild=T;
		pre=Thrt;
		InThreading(T);
		pre->rchild=Thrt;
		pre->RTag=1;
		Thrt->rchild=pre;
	}
}

线索二叉树
此中序遍历从H开始,序列为HDIBJEAFCG,通过I可以直接得到I的前驱为D,后继为B。而不需要再次遍历找到。


四、中序遍历线索二叉树

功能:中序遍历线索二叉树
实现:
①另p=T->lchild,将p指向树根结点,循环遍历当p=T时说明回到了头结点即遍历结束。
②当p!=T时,首先移动到最深的左儿子结点,输出数据。接着判断RTag若为1,则说明p的rchild指针指向中序遍历的下一个结点,于是按照右线索访问后继结点
③p=p->rchild即转向p的右子树继续遍历,直到回到头结点结束。

void InOrderTraverse_Thr(BiThrTree T)
{
	BiThrTree p;
	p=T->lchild;
	while (p!=T)
	{
		while (p->LTag==0) p=p->lchild;
		cout<<p->data;
		while (p->RTag==1&&p->rchild!=T)
		{
			p=p->rchild;
			cout<<p->data;
		}
		p=p->rchild;
	}
}

线索二叉树
如图为带有头结点的双向二叉链表,灰色箭头为前驱,黑色箭头为后继


总结

本篇介绍了线索二叉树的结构和中序线索化二叉树,中序线索化带有头结点的二叉树,最后中序遍历线索二叉树。
线索二叉树的应用可以方便地得到节点的前驱和后继信息。

测试代码及运行实例

int main()
{
	BiThrTree T,M;
	PreCreateBiTree(T);
	InOrderThreading(M,T);
	InOrderTraverse_Thr(M);
	return 0;
}

线索二叉树

上一篇:二叉查找树练习题


下一篇:js es6 Proxy