树(四)——线索二叉树

目录


一、线索二叉树基本概念

1、概念 

     二叉链表的存储结构,只能找到该结点的左右孩子,不能得到该结点在遍历过程中的遍历前驱和直接后继结点。二叉链表存储二叉树时,有2n个指针域,其中n+1个都为空指针域。

利用空指针域存储结点遍历过程中的前驱和后继结点,使结点之间组成联系,在遍历的过程中可以不用通过栈或递归。

线索 二叉树的作用:

将树转换成线性链表,二叉树在首次遍历中线索化,在需要时可以直接获得结点的前驱和后继,不需要像栈一样频繁的压栈出栈。

2、线索二叉树的结构

线索二叉树与二叉链表的区别是,增加了左标志域Ltag,和右标志域Rtag,,是两个布尔值的数据域。线索二叉树的结构为:

1、若结点的左孩子不为空,LChild指针域仍指向其左孩子,否则,LChild指针域指向遍历过程序列的前驱结点。

2、若结点的右孩子不为空,RChild指针域仍指向其右孩子,否则,RChild指针域指向遍历过程序列的后继结点。

3、对于Ltag和Rtag的标志域的定义:

   Ltag = 0时,LChild域指向结点的左孩子。

   Ltag=1时, LChild域指向结点的遍历前驱。

   Rtag=0时,RChild域指向结点的右孩子。

   Rtag=1时,RChild域指向结点的遍历后继。

typedef struct Node
{
   TypeData data;
   int Ltag,Rtag;
   struct Node*LChild;
   Struct Node*RChild;
}BiTree,*BiThrTree;

3、名词解释

线索:指向前驱和后继结点的指针

线索化:对二叉树以先序次序(中序,后序)遍历的过程中将结点的空指针域改为线索的过程,叫做‘线索化’。

线索二叉树:经过线索化的二叉树。

线索链表:以线索二叉树的结点结构存储的含有线索的二叉链表

二、线索二叉树的线索化

1、原理

线索化的过程就是在遍历的过程中将二叉树中空的指针域填上结点的直接前驱或直接后继的过程。

1.1如何实现空指针域中结点的前驱或后继

     结点空指针域前驱的填入,需要知道刚刚访问的结点,后继的填入,需要知道下一个访问的结点,所以可以用一个 pre 指针专门用于记录刚刚访问的结点(也就是现在访问的结点的上一个结点。

     对于 pre 指针,因为开始遍历的第一个结点没有前驱,所以 pre 需要初始化为NULL

对于空指针域结点的前驱的填入思路:

     如果当前访问的结点的左子树为空,此时指向左子树的指针域为空,让其指向刚刚访问过的结点pre,root->LChild=pre 同时将左标志域 Ltag 置为 1 ,即完成了前驱线索。

对于后继的填入思路:

      结点的右子树为空,指向其右子树的指针域为空,让其指向下一个结点,就需要知道下一个结点的存在才能进行连接。所以需要在遍历到下一个结点时,让刚访问过的结点 pre 进行回填指向右孩子的指针域。

      pre是刚访问过的结点,判断pre的右孩子是否为空,为空时:root是当前访问的结点,对pre的右孩子指针域进行回填,让pre->RChild=root,且将右标志域Rtag 置为1。

1.2 图解便于理解

2、算法实现

二叉树的中序线索化算法

//中序线索化算法
void Inthread(BiTree root)
{
  if(root!=NULL)
    {
       Inthread(root->LChild);   //通过递归线索化左子树
       if(root->LChild==NULL)    //当前结点的左子树为空
         {
            root->LChild=pre;    //  第一步   置前驱线索
            root->Ltag=1;        //左标志域为1
         }
        if(pre!=NULL&&pre->RChild==NULL)  //刚刚访问的结点不为空且右子树为空
          {
             pre->RChild=root;        //  第二步 置后驱线索
             pre->Rtag=1;        //右标志域为1
          }
        pre=root;               //  第三步  pre指向当前访问的结点
        Inthread(root->RChild);  //递归线索化右子树
     }
}

三、线索二叉树的遍历

线索二叉树的遍历过程分为两步:

1、第一步找到遍历过程中的第一个被访问的结点。

2、第二步是不断在遍历的过程中寻找刚访问结点的后继结点,并访问。

1、中序线索二叉树中寻找遍历的首结点 

中序次序是先访问左子树,根结点、右子树。所以中序遍历的首结点是左子树的最左下端的结点。

BiThrTree InFirst(BiThrTree bt)
{
      BiThrTree p=bt;
      if(p==NULL)
        return NULL;
      while(p->Ltag==0)
           p=p->LChild;
      return p;
}

2、寻找结点的直接后继

要找 p 结点的后继,需要进入其右子树,p结点的后继结点是右子树中遍历的第一个结点也就是左子树中的最左下端。

BiThrTree InNext(BiThrTree p)
{ 
    if(p->Rtag==1)        //如果p的右子树为空,直接利用线索找到其后继结点。
      next=p->RChild;
    else                 //在p的右子树中找左子树的第一个结点
     { 
          next=p->RChild;
          while(next->Ltag==0)    //结点的左子树指向下一个结点时,继续找最左下端的左子树
          next=next->LChild;
     }
    return next;
}

3、遍历线索二叉树

按照线索二叉树遍历的两步走

void TinOrder(BiThrTree root)
{
    BiThrTree p;
    p=InFirst(root);
    while(p!=NULL)
    {
       Visit(p->data);
       p=InNext(p);
     }
}

四、线索二叉树遍历的应用

算法实现:

#include<stdio.h>
#include<stdlib.h>
//定义结点结构
typedef struct Node
{
	char data;
	int Ltag, Rtag;
	struct Node* LChild;
	struct Node* RChild;
}BiTreeNode,*BiThrTree;
//建立二叉链表
void Establish(BiThrTree* root)  
{
	char demo;
	demo = getchar();
	if (demo == '^')
		*root = NULL;
	else
	{
		*root = (BiThrTree)malloc(sizeof(BiTreeNode));
		if (*root == NULL)
			return;
		(*root)->data = demo;
		(*root)->Ltag = 0;
		(*root)->Rtag = 0;
		Establish(&((*root)->LChild));
		Establish(&((*root)->RChild));
	}
}
//线索化,建立线索链表
void Inthread(BiThrTree*root)
{
	static BiThrTree pre = NULL;       //在递归调用中,初始化pre,且保存每次pre的信息,用static静态局部变量解决
	if ((*root) != NULL)
	{
		Inthread(&((*root)->LChild));
		if ((*root)->LChild == NULL)
		{
			(*root)->LChild = pre;
			(*root)->Ltag = 1;
		}
		if (pre != NULL && pre->RChild == NULL)
		{
			pre->RChild = (*root);
			pre->Rtag = 1;
		}
		pre = (*root);
		Inthread(&((*root)->RChild));
	}
}
//查找遍历的首结点
BiThrTree InFirst(BiThrTree root)
{
	BiThrTree p=root;
	if (p == NULL)
		return NULL;
	while(p->Ltag == 0)
		 p = p->LChild;
	return p;
}
//查找遍历过程中结点的直接后继
BiThrTree InNext(BiThrTree p)
{
    BiThrTree next=NULL;
	if (p->Rtag == 1)
		next = p->RChild;
	else
	{
		next= p->RChild;
		while (next!=NULL&&next->Ltag == 0)   //next!=NULL,解决局部变量next为空指针时,不能访问data等数据
			next = next->LChild;
	}
	return next;
}
//遍历线索二叉树
void TinOrder(BiThrTree root)
{
	BiThrTree p;
	if (root == NULL)
		return;
	p = InFirst(root);
	while (p != NULL)
	{
		printf("%c ", p->data);
		p = InNext(p);
	}
}
main()
{
	BiThrTree root;
	printf("------根据二叉链表的格式输入结点数据------\n");
	printf("\n");
	Establish(&root);
	Inthread(&root);
	printf("-------遍历打印线索二叉树-------\n");
	printf("\n");
	TinOrder(root);
}

运行结果:

                      

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2023年12月12日
下一篇 2023年12月12日

相关推荐