线索二叉树基本概念,举例理解线索二叉树,遍历中序线索二叉树

为什么要研究线索二叉树?

当用二叉链表作为二叉树的存储结构时,可以很方便地找到某个结点的左右孩子;但一般情况下,无法直接找到该结点在某种遍历序列中的前驱和后继结点。

之前学到的二叉树链表中空指针域的数量:具有n个结点的二叉链表中,共有n+1个指针域为空


线索二叉树基本概念

利用二叉链表中的空指针域:

如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继

这种改变指向的指针称为线索
加上线索的二叉树称为线索二叉树
对二叉树按某种遍历次序使其变为线索二叉树的过程叫线索化

为了区分lchild和rchild指针到底是指向左右孩子的指针还是指向前驱或者后继的指针,对二叉链表中每个结点增设两个标志域ltag和rtag,并约定:
在这里插入图片描述
结点形式为:
在这里插入图片描述


线索二叉树存储表示

typedef struct BiThrNode{
	int data;
	int ltag,rtag;//左右标志
	struct BiThrNode *lchild,rchild;//左右孩子指针
}BiThrNode,*BiThrTree;	

举例理解三种线索二叉树

红色虚线指向前驱和后继,蓝色实线指向左右孩子

在这里插入图片描述
1)先序线索二叉树
先序序列为:A B C D E
图解为:
在这里插入图片描述
2)中序线索二叉树
中序序列:B C A E D
图解是:
在这里插入图片描述
3)后续线索二叉树
后序序列:C B E D A
图解为:
在这里插入图片描述


为什么要增设头结点?

下图为中序线索二叉树
中序序列为H D I B E A F C G
在这里插入图片描述
由上图可见,还剩余两个空指针。为了操作方便,所以避免悬空态,我们增设一个头结点:

对头结点的操作:
1)data域置为空,ltag=0,lchild指向根结点
2)rtag=1,rchild指向遍历序列中最后一个结点
3)遍历序列中第一个结点(无前驱)的lchild域和最后一个结点(无后继)的rchild域都指向头结点。

最后结果如图:
在这里插入图片描述

像循环链表一样,可以访问任意结点,操作比较方便。


遍历中序线索二叉树
先声明存储表示:

typedef struct BiThrNode{
	 int data;
	 int ltag,rtag;//左右标志
	 struct BiThrNode *lchild,rchild;//左右孩子指针
}BiThrNode,*BiThrTree; 

【算法描述】

void InOrderTraverse_Thr(BiThrTree T)
{//T指向头结点,头结点的左链lchild指向根结点
//中序遍历二叉线索树T的非递归算法,对每个数据元素直接输出
	BiThrNode *p=T->lchild;	//p指向根结点
	while(p!=T)		//空树或遍历结束时,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;		//转向p的右子树
	}	
}

时间复杂度为O(n),空间复杂度为O(1),因为线索二叉树的遍历不需要栈来实现递归操作


内容参考:《数据结构》严蔚敏

发布了34 篇原创文章 · 获赞 85 · 访问量 4609

猜你喜欢

转载自blog.csdn.net/weixin_45895026/article/details/104064190