树 —— 线索二叉树

线索二叉树的定义

充分利用二叉链表中的空链域,将遍历过程中结点的前驱、后继信息保存下来。

  • 若结点有左子树,则其 LChild 域指向其 左孩子,否则 LChild 域指向其 前驱结点

  • 若结点有右子树,则其 RChild 域指向其 右孩子,否则 RChild 域指向其 后继结点
    树 —— 线索二叉树_第1张图片

  • 线索:在这种存储结构中,指向前驱和后继结点的指针叫做线索。

  • 线索链表:以这种结构组成的二叉链表作为二叉树的存储结构,叫做线索链表。

  • 线索化:对二叉树以某种次序进行遍历并且加上线索的过程叫做线索化。

  • 线索二叉树:线索化了的二叉树称为线索二叉树。

typedef struct TBTNode
{
	char data;
	int ltag,rtag;
	struct TBTNode *lchild;
	struct TBTNode *rchild;
}TBTNode;

树 —— 线索二叉树_第2张图片


二叉树中序线索化

中序遍历 {H、D、I、B、J、E、A、F、C、G}

分成三个步骤

(1)左子树线索化
(2)建立当前结点 p 与前驱结点 pre 的线索
(3)右子树线索化

注意:每次线索化之前需要确定好当前结点 p 和前驱结点 pre,即按照中序遍历的顺序

  1. p 结点是H,pre 结点是NULL —— 对应步骤(1)
  2. p 结点是D,pre 结点是H —— 对应步骤(2)
  3. p 结点是I,pre 结点是D —— 对应步骤(3)
  4. p 结点是B,pre 结点I —— 对应步骤(2)

(1)建立中序线索二叉树

void InThread(TBTNode *p,TBTNode *&pre)
{
	if(p!=NULL)
	{
		InThread(p->lchild,pre);	// 递归,左子树线索化
		if(p->lchild==NULL)		// 建立当前结点的前驱线索
		{
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL)	//建立前驱结点的后继线索
		{
			pre->rchild=p;
			pre->rtag=1;
		}
		pre=p;
		InThread(p->rchild,pre);	// 递归,右子树线索化
	}
}
void createInThread(TBTNode *root)
{
	TBTNode *pre=NULL;	//	前驱结点指针
	if(root!=NULL)
	{
		InThread(root,pre);
		pre->rchild=NULL;	// 处理最后一个结点的后继
		pre->rtag=1;
	}
}

(2)遍历中序线索二叉树

TBTNode *First(TBTNode *p)	//	找到最左下的结点
{
	while(p->ltag==0)
		p=p->lchild;
	return p;
}

TBTNode *Next(TBTNode *p)
{
	if(p->rtag==0)	//	如果p存在右孩子,则p的后继结点是p的右子树中的最左下结点
		return First(p->rchild);
	return p;
}

void Inorder(TBTNode *root)
{
	for(TBTNode *p=First(root);p!=NULL;p=Next(p))
		Visit(p);
}

二叉树前序线索化

前序遍历 {A、B、D、H、I、E、J、C、F、G}

分成三个步骤

(1)建立当前结点 p 与前驱结点 pre 的线索
(2)左子树线索化
(3)右子树线索化

注意:每次线索化之前需要确定好当前结点 p 和前驱结点 pre,即按照中序遍历的顺序

  1. p 结点是A,pre 结点是NULL —— 对应步骤(1)
  2. p 结点是B,pre 结点是A —— 对应步骤(2)
  3. p 结点是D,pre 结点是B —— 对应步骤(3)
  4. p 结点是H,pre 结点D —— 对应步骤(2)

(1)建立前序线索二叉树

void preThread(TBTNode *p,TBTNode *&pre)
{
	if(p!=NULL)
	{
		if(p->lchild==NULL)		// 建立当前结点的前驱线索
		{
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL)	//建立前驱结点的后继线索
		{
			pre->rchild=p;
			pre->rtag=1;
		}
		pre=p;
		preThread(p->lchild,pre);	// 递归,左子树线索化
		preThread(p->rchild,pre);	// 递归,右子树线索化
	}
}
void createPreThread(TBTNode *root)
{
	TBTNode *pre=NULL;	//	前驱结点指针
	if(root!=NULL)
	{
		preThread(root,pre);
		pre->rchild=NULL;	// 处理最后一个结点的后续
		pre->rtag=1;
	}
}

(2)遍历前序线索二叉树

void preorder(TBTNode *root)
{
	if(root!=NULL)
	{
		TBTNode *p=root;
		while(p!=NULL)
		{
			while(p->ltag==0)	//	存在左孩子,则边访问边左移
			{
				Visit(p);
				p=p->lchild;
			}
			Visit(p);	//	不存在左孩子,只访问不左移
			p=p->rchild;	//	因为不存在左孩子,所以向其右孩子或者后继结点移动
		}
	}
}

二叉树后序线索化

后序遍历 {H、I、D、J、E、B、F、G、C、A}

分成三个步骤

(1)左子树线索化
(2)右子树线索化
(3)建立当前结点 p 与前驱结点 pre 的线索

注意:每次线索化之前需要确定好当前结点 p 和前驱结点 pre,即按照中序遍历的顺序

  1. p 结点是H,pre 结点是NULL —— 对应步骤(1)
  2. p 结点是I,pre 结点是H —— 对应步骤(2)
  3. p 结点是D,pre 结点是I —— 对应步骤(3)
  4. p 结点是J,pre 结点D —— 对应步骤(2)

(1)建立后序线索二叉树

void postThread(TBTNode *p,TBTNode *&pre)
{
	if(p!=NULL)
	{
		preThread(p->lchild,pre);	// 递归,左子树线索化
		preThread(p->rchild,pre);	// 递归,右子树线索化
		if(p->lchild==NULL)		// 建立当前结点的前驱线索
		{
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL)	//建立前驱结点的后继线索
		{
			pre->rchild=p;
			pre->rtag=1;
		}
		pre=p;
	}
}
void createPostThread(TBTNode *root)
{
	TBTNode *pre=NULL;	//	前驱结点指针
	if(root!=NULL)
	{
		postThread(root,pre);
		pre->rchild=NULL;	// 处理最后一个结点的后续
		pre->rtag=1;
	}
}

你可能感兴趣的:(数据结构)