线索二叉树建立及遍历(C实现)

本文深入探讨了线索二叉树的概念及其在二叉树遍历中的应用,详细讲解了如何在二叉树中添加线索以方便获取节点的前驱和后继,并提供了中序、前序和后序线索二叉树的图解与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线索二叉树建立及遍历

线索二叉树概念

二叉树通过二叉链表作为存储结构时,只能得到当前结点的左右孩子信息,而无法得到结点在任一序列的前驱和后继,如果想要保存遍历中的信息,需要在每个结点上添加两个指针域,分别指向当前结点的前驱和后继,指向线性序列中的前驱和后继的指针,称为线索,
并且增加两个标志域 如下图

在这里插入图片描述
设置规定如下:

ltag=0 表示lchild 指向左孩子
latg=1 表示lchild 指向前驱
rtag=0 表示rchild 指向右孩子
rtag=1 表示rchild 指向后继

按照上述的原则 在二叉树中添加线索的过程叫二叉树的线索化

线索二叉树图解

假设给定下图的二叉树
在这里插入图片描述
这时可以根据中序遍历的结果来对二叉树线索化 ,中序遍历的结果为:DGBAECF
实线指的是二叉树所指向的结点
虚线指的是线索 如下图:
在这里插入图片描述
解释一下 建立线索的过程 :

  1. A节点左右孩子存在,ltag=0,lchild指向左孩子B,rtag=0 ,rchild指向右孩子C。
  2. B节点左孩子存在 ltag=0,lchild指向左孩子D,右孩子不存在 rtag=1,根据中序遍历的结果B的后继为A,rchild 指向后继结点A。
  3. C节点左右孩子存在,ltag=0,lchild指向左孩子E, rtag=0 rchild指向右孩子F。
  4. D节点无左孩子,ltag=1,根据中序遍历的结果D之前没有元素 lchild指向前驱根节点,rtag=0 ,rchild 指向右孩子G。
  5. E节点不存在左右孩子 ltag=1 ,根据中序遍历E的前驱为A,lchild 指向前驱A rtag=1 根据中序遍历E的后继为C,rchild 指向后继C。
  6. F节点不存在左右孩子 ltag=1,根据中序遍历F的前驱为c,lchild指向前驱C, rtag=1,根据中序遍历F的后继为根 rchild 指向后继根
  7. G节点不存在左右孩子,ltag=1,根据中序遍历G的前驱为D,lchild指向前驱D,rtag=1,根据中序遍历G的后继为B ,rchild 指向后继B

同理可以得到前序遍历线索二叉树为:
在这里插入图片描述
解释一下过程:

  1. 节点A存在左右孩子,lchild指向B,rtag=0 ,rchild指向C。
  2. 节点B有左孩子,ltag=0,lchild指向D,无右孩子 rtag=1,由前序遍历得,rchild指向后继D。
  3. 节点C存在左右孩子,ltag=0 lchild指向E,rtag=0 ,rchild指向F。
  4. 节点D无左孩子,ltag=1,由前序遍历得,lchild指向前驱B,有有孩子 rtag=0,rchild指向后继G。
  5. 节点E无左右孩子,ltag=1,由前序遍历得,lchild指向前驱C,rtag=1,由前序遍历得,lchild指向后继F。
  6. 节点F无左右孩子,ltag=1,由前序遍历得,lchild指向前驱E,rtag=1,由前序遍历得,lchild指向后继根。
  7. 节点G无左右孩子,ltag=1,由前序遍历得,lchild指向前驱D,rtag=1,由前序遍历得,lchild指向后继C。

如图后序的线索二叉树为:
在这里插入图片描述
解释一下过程:

  1. 节点A存在左右孩子,lchild指向B,rtag=0 ,rchild指向C。
  2. 节点B有左孩子,ltag=0,lchild指向D,无右孩子 rtag=1,由后序遍历得,rchild指向后继E。
  3. 节点C存在左右孩子,ltag=0 lchild指向E,rtag=0 ,rchild指向F。
  4. 节点D无左孩子,ltag=1,由后序遍历得,lchild指向前驱G,有右孩子 rtag=0,rchild指向后继G。
  5. 节点E无左右孩子,ltag=1,由后序遍历得,lchild指向前驱B,rtag=1,由后序遍历得,lchild指向后继F。
  6. 节点F无左右孩子,ltag=1,由后序遍历得,lchild指向前驱E,rtag=1,由后序遍历得,lchild指向后继C。
  7. 节点G无左右孩子,ltag=1,由后序遍历得,lchild指向前驱根,rtag=1,由后序遍历得,lchild指向后继D。

线索二叉树代码实现

大致知道了线索是怎么回事,看看以中序为例代码的实现方式:

#include <stdio.h>
#include <stdlib.h>
typedef struct BiThtree{
    char data;
    int rtag,ltag;
    struct BiThtree *Lchild,*Rchild;
}Bitree, *BiTHree;

BiTHree pre;                // 设置临时结点,保存之前访问的节点

void InitTree(BiTHree *T){ //二叉树的初始化
    char  c;
    scanf("%c",&c);
    if(c==' '){
        *T=NULL;
    }else{
        *T=(Bitree*)malloc(sizeof(Bitree));
        (*T)->data=c;
        (*T)->ltag=0;
        (*T)->rtag=0;
        InitTree(&(*T)->Lchild);
        InitTree(&(*T)->Rchild);
    }
}
//中序线索化二叉树
void Inthreading(BiTHree T){
    if(T){
        Inthreading(T->Lchild);
        if(!T->Lchild){    						//左孩子不存在时 
            T->ltag=1;
            T->Lchild=pre; 						//Lchild指向刚刚访问的节点
        }
        if(!pre->Rchild){ 						//右孩子不存时
            pre->rtag=1;
            pre->Rchild=T;						//Rchild指向上一个访问的节点 
        }
        pre=T;  								//访问结束后将上一个节点赋值给pre
        Inthreading(T->Rchild);
    }
}
/*设置根节点,二叉树线索化是动态的过程,中序遍历时第一个元素的前驱指向根节点,末尾元素的后继也指向根。*/
void Inorderthreading(BiTHree *root, BiTHree T)
{
     *root =(Bitree*)malloc(sizeof(BiTHree));
     (*root)->ltag=0;
     (*root)->rtag=1;
     if(!T){
         (*root)->Lchild=*root;
     }else{
        (*root)->Lchild=T;
        pre=*root;
        Inthreading(T);          				//线索化完成pre为最后一个节点
        pre->Rchild=*root;      		 		//末尾元素指向根
        pre->rtag=1;             				//标记为1
        (*root)->Rchild=pre;     				//根的右孩子指向末尾元素
     }
}
void visit(char c)                  
{
    printf("%c",c);
}
void InOrderTraverse( BiTHree T )    			//以迭代的方式遍历二叉树
{
    BiTHree p;
	p = T->Lchild;
	while( p != T )
	{
		while( p->ltag ==0)
		{
			p = p->Lchild;
		}
		visit(p->data);
		while( p->rtag == 1 && p->Rchild != T )
		{
			p = p->Rchild;
			visit(p->data);
		}
		p = p->Rchild;
	}
}
int main()
{

    BiTHree T =NULL,root=NULL;
    InitTree(&T);
    Inorderthreading(&root,T);
    printf("中序遍历为:\n");
    InOrderTraverse(root );
    return 0;
}

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值