二叉线索树的先序、中序、后序的线索化及其遍历

线索二叉树

注意:源码

  • 二叉线索树的概念
    二叉线索树是在传统二叉树结构的基础上,加上判断结点左右孩子是否为空的标志–LTag,RTag。
    当左孩子为空的时候,lchild指向该节点的前驱结点,当右孩子为空的时候,rchild指向该节点后继结点。以此提高链表的储存密度。下面我们给出二叉线索树的结构:
	typedef struct BiTNode
	{
    
    
		char data;//数据域
		int LTag = 0;//左标签
		int RTag = 0;//右标签
		struct BiTNode* lchild = nullptr;//指针必须初始化,c11标准
		struct BiTNode* rchild = nullptr;//左右孩子
	}BiTNode, * BiTree;
  • 二叉线索树相对于一般二叉树的优点
    当以二叉链表作为储存结构,只能找到结点的左右孩子信息,而不能得到结点在任意序列中的前驱和后继信息,这种信息只有在遍历的动态过程中才能得到,而二叉线索树可以保存这些动态过程中的前驱和后继信息。而且由于一般二叉树有n个结点必定存在(n+1)的空链域(容易证得)。因此可以充分利用这些空链域储存前驱和后继信息,提高储存密度。

  • 创建二叉树
    我们以先序遍历创建二叉树,先输入根节点,再创建左子树,在创建右子树(若结点data为#,表示空树)。代码如下:

	void CreateBiTree(BiTree& T,BiTree P)//先序输入 
	{
    
    
		char ch;
		cin >> ch;
		if (ch == '#') T = NULL;
		else
		{
    
    
			T = new BiTNode;//分配空间
			T->data = ch;//根节点赋值
			T->parent = P;
			CreateBiTree(T->lchild,T);//建立左子树
			CreateBiTree(T->rchild,T);//建立右子树
		}
	}
  • 先序线索化二叉树
    按照根左右的顺序进行先序线索化,具体流程为:
    首先,我们先创建一个空的BiTree Thrt (一方面用于作为第一个结点的前驱结点,最后一个节点的后继结点,另一方面也可以将二叉树形成一个双向链表,既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历,非常方便-^- ^-),创建一个BiTree pre 始终指向刚刚访问过的结点。
    其次,我们先找到根结点,若此结点没有左孩子,我们将lchild指向它的前驱,并将此节点赋给pre。若pre没有右孩子,我们将pre的rchild指向当前节点。
    最后,递归左子树,再递归右子树。
    我们所要遍历的树如下所示:

在这里插入图片描述
先序线索化后的二叉树就是这个样子(有点丑 -^- ^- ):
在这里插入图片描述
下面附上代码(pre是一个BiTree类型的全局变量,如果不要带头结点的线索树,就将第一个最左面的结点的lchild置空,之后pre指向此节点):

//以结点T为根的子树先序线索化
void PreThreading(BiTree& T)
{
    
    
	if (T)//T非空
	{
    
    
		if (!T->lchild)//左子树为空
		{
    
    
			T->LTag = 1;
			T->lchild = pre;//指向前驱结点
		}
		else
		{
    
    
			T->LTag = 0;
		}
		if (!pre->rchild)
		{
    
    
			pre->RTag = 1;
			pre->rchild = T;//指向后继结点
		}
		else
		{
    
    
			T->RTag = 0;
		}
		pre = T;//pre指向当前节点
		if (T->LTag == 0)//这点很重要,只有结点有左孩子的时候才可以遍历左子树,否则会陷入死循环
		{
    
    
			PreThreading(T->lchild);//遍历左子树
		}
		if (T->RTag == 0)//同上
		{
    
    
			PreThreading(T->rchild);//遍历右子树
		}
	}
}
//以结点T为根的字树先序线索化

//带头节点的二叉树先序线索化
void PreOrderThreading(BiTree& Thrt, BiTree T)
{
    
    
	Thrt = new BiTNode;//头节点
	Thrt->LTag = 0;//左标签为零,表明有左子树
	Thrt->RTag = 1;
	Thrt->rchild = Thrt;//右子树指向自己
	if (!T)//若为空树,右子树指向自己
	{
    
    
		Thrt->lchild = Thrt;
	}
	else
	{
    
    
		Thrt->lchild = T;//左指针指向T
		pre = Thrt;
		PreThreading(T);//进行线索化
		pre->rchild = Thrt;//此时pre为最右面的那个节点,让它的右子树为Thrt
		pre->RTag = 1;
		Thrt->rchild = pre;//Thrt的右子树为pre,形成闭环
	}
}
//带头节点的二叉树先序线索化

此时,二叉树的先序线索化就结束了。

  • 中序线索化二叉树
    中序线索化二叉树的步骤和先序二叉树很相似,具体步骤为:
    1.遍历左子树,找到最左面的结点,让他的lchild指向pre,并将pre指向当前节点。
    2.对根节点重复以上步骤。
    3.在遍历右子树。
    下面是中序线索化二叉树的图(图依旧很丑):
    在这里插入图片描述
    下面附上代码(代码还是那个代码,只不过换了一下顺序):
//以结点T为根的子树中序线索化
void InThreading(BiTree &T)
{
    
    
	if (T)//T非空
	{
    
    
		InThreading(T->lchild);//遍历左子树
		if (!T->lchild)//左子树为空
		{
    
    
			T->LTag = 1;
			T->lchild = pre;
		}
		else
		{
    
    
			T->LTag = 0;
		}
		if (!pre->rchild)
		{
    
    
			pre->RTag = 1;
			pre->rchild = T;
		}
		else
		{
    
    
			T->RTag = 0;
		}
		pre = T;
		InThreading(T->rchild);//遍历右子树
	}
}
//以结点T为根的字树中序线索化

//带头节点的二叉树中序线索化
void InOrderThreading(BiTree& Thrt, BiTree T)
{
    
    
	Thrt = new BiTNode;
	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;
	}
}
//带头节点的二叉树中序线索化
  • 后序线索化二叉树
    后序线索化二叉树和前面的思路一样:
    1.先遍历左子树,找到最左面的结点,若其lchild为空,这lchild 指向pre,若pre的rchild为空,则pre的rchild指向当前结点。
    2.遍历右子树,进行上面的步骤。
    3.最后再对根节点进行如上操作。
    下面是后序线索化二叉树的图(图还是很丑):
    在这里插入图片描述
    附上代码:
//以结点T为根的子树后序线索化
void PosThreading(BiTree& T)
{
    
    
	if (T)//T非空
	{
    
    
		PosThreading(T->lchild);//遍历左子树
		PosThreading(T->rchild);//遍历右子树
		if (!T->lchild)//左子树为空
		{
    
    
			T->LTag = 1;
			T->lchild = pre;
		}
		else
		{
    
    
			T->LTag = 0;
		}
		if (!pre->rchild)
		{
    
    
			pre->RTag = 1;
			pre->rchild = T;
		}
		else
		{
    
    
			T->RTag = 0;
		}
		pre = T;
	}
}
//以结点T为根的字树后序线索化

//带头节点的二叉树后序线索化
	void PosOrderThreading(BiTree& Thrt, BiTree T)
	{
    
    
		Thrt = new BiTNode;
		Thrt->LTag = 0;
		Thrt->RTag = 1;
		Thrt->rchild = T;
		if (!T)
		{
    
    
			Thrt->lchild = Thrt;
		}
		else
		{
    
    
			Thrt->lchild = T;
			pre = Thrt; 
			PosThreading(T);
		}
	}
	//带头节点的二叉树后序线索化

看到这里,二叉线索树的线索化就算结束了,当我们的故事还没有结束(生活不止眼前的线索化,还有二叉树的遍历)。下面我们来谈一谈二叉树线索树 的遍历。

  • 遍历先序线索树
    先序遍历线索树时,首先定义一个BiTree p,指向头节点p 的左子树,即 p = T->child。
    然后,我们要输出p的信息,如果该节点有左子树,便p指向左子树,并输出结点信息,若没有左子树,便p指向右子树,输出信息。
    最后,当p指向T,循环结束。
    下面是先序遍历的图例(动画做的不好):
    在这里插入图片描述
    下面附上代码(就是如此简单):
	//遍历先序线索二叉树
	void PreTraverse_Thr(BiTree T)
	{
    
    
		BiTree p = T->lchild;
		while (p != T)
		{
    
    
			cout << p->data;
			if (p->LTag == 0)
			{
    
    
				p = p->lchild;
			}
			else
			{
    
    
				p = p->rchild;
			}
		}
	}
//遍历先序线索二叉树
  • 遍历中序线索树
    遍历中序线索树的思路就是按照左根右的思路:
    首先,先找到最左面的结点p,输出节点值。
    然后,若左面的结点的rchild=1,p = p->rchild,输出结点值,循环往复。
    若遍历到根节点p,p = p->rchild。在循环往复,直至p为T结束。
    下面是中序遍历的图例(动画依旧做的不好):
    在这里插入图片描述
    附上代码(依旧那么简单):
//遍历中序线索二叉树
void InTraverse_Thr(BiTree T)
{
    
    
	BiTree 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;
	}
}
//遍历中序线索二叉树
  • 遍历后序线索树
    遍历后序线索树有点难搞呀。原先的二叉树结构需要做一下改动,我们要为每个结点添加父节点,以解决当遍历到右子树时无法访问父节点。
    代码如下:
	typedef struct BiTNode
	{
    
    
		char data;//数据域
		int LTag = 0;//左标签
		int RTag = 0;//右标签
		struct BiTNode* lchild = nullptr;//指针必须初始化,c11标准
		struct BiTNode* rchild = nullptr;//左右孩子
		struct BiTNode* parent = nullptr;//双亲
	}BiTNode, * BiTree;

有了上面的结构,后序遍历线索树就简单多了。
1.首先我们应该像中序遍历一样,先找到最左面的结点p,输出节点的值,若p->rchild == 1;p = p->rchild,输出节点的值,一直循环往复,直到p->lchild == 0 并且 p->rchild == 0。
2.此时p为根节点(相对来说),我们需要靠双亲结点继续遍历,并输出节点的值。直到我们找到真正的根节点p,此时p = p->rchild。
3.重复1,2步骤,直到p的双亲结点为空。
演示图列:
在这里插入图片描述
附上代码(这次不简单哦):

//后序遍历需要双亲节点
void PosTraverse_Thr(BiTree T)
{
    
    
	BiTree root = T->lchild;//初始节点
	BiTree prev = nullptr;//记录上一个节点
	while (root)
	{
    
    
		if (root->lchild == root->rchild)//如果双亲没有左子树或者右子树
		{
    
    
			root = root->rchild;
		}
		while (root->LTag == 0 && root->lchild != prev)//找到最左边的节点
		{
    
    
			root = root->lchild;
		}
		while (root->RTag == 1)//输出并遍历节点的后继
		{
    
    
			cout << root->data;
			prev = root;//记录上一个节点
			root = root->rchild;
		}
		if (root == T->lchild)//判断是不是根节点
		{
    
    
			cout << root->data;
			return;
		}
		while (root->rchild == prev)//不是根节点,访问当前节点的双亲节点
		{
    
    
			cout << root->data;
			prev = root;
			root = root->parent;
			if (root == nullptr)//遍历到根节点,退出
			{
    
    
				return;
			}
		}
		if (root->RTag == 0)//遍历右子树
		{
    
    
			root = root->rchild;
		}
	}
}
//遍历后序线索二叉树

猜你喜欢

转载自blog.csdn.net/m0_43456002/article/details/104268875