二叉线索树的创建

二叉线索树是在二叉树节点上添加线索,便于快速查找前驱和后继节点的数据结构。文章介绍了如何通过先序、中序和后序遍历来线索化二叉树,提供了相应的C语言实现代码,包括中序、先序和后序线索化的详细步骤和示例。

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

何为二叉线索树?

        在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化

typedef int TDataType;

typedef struct TreeNode {
    TDataType val;
    int ltag, rtag;
    struct TreeNode* left, * right;
}TreeNode;

//注意:二叉搜索树与普通二叉树的区别在于结构体内多了两个成员ltag和rtag

//ltag=0,则left指向左孩子
//ltag=1,则left指向前驱节点

//rtag=0,则right指向右孩子
//rtag=1,则right指向后继节点

提示:二叉线索树的创建其实和正常二叉树的创建一样,只是多了一个线索化的过程。

二叉线索树一共有三种,分别以先序、中序、后序得到排列顺序为依据,得到节点的前驱和后继

以中序遍历为例子,图如下

 


void Visit(TreeNode* root,TreeNode**prev)
{
    //root代表当前节点,*prev代表遍历root之前的一个节点
    //指针指向前驱和后继的条件是为空
    if (root->left == NULL)
    {
        root->left = *prev;//让当前节点的左指针指向前驱
        root->ltag = 1;
    }
    if (*prev != NULL && (*prev)->right == NULL)
    {
        (*prev)->right = root;//让前一个指针的右节点指向它的后继
        (*prev)->rtag = 1;
    }
    *prev = root;
}

//中序线索化二叉树
void InCreateT(TreeNode*root,TreeNode**prev)
{
    if (root == NULL)
        return;
    //if (root->ltag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
        InCreateT(root->left, prev);
    Visit(root, prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
    //if (root->rtag == 0)//可加可不加---因为root的右指针没有被改变
        InCreateT(root->right, prev);
}

void InThread(TreeNode* root)
{
    if (root)
    {
        TreeNode* prev = NULL;
        InCreateT(root, &prev);
        //结束时,最后一个遍历的节点右指针是没有被判断是否该被初始化的,故在结束时要另行判断
        if (prev->right == NULL)
            prev->rtag = 1;
    }
}

之后的先序线索二叉树,后序线索二叉树的思路与中序一样

先序代码如下

void Visit(TreeNode* root,TreeNode**prev)
{
    //root代表当前节点,*prev代表遍历root之前的一个节点
    //指针指向前驱和后继的条件是为空
    if (root->left == NULL)
    {
        root->left = *prev;//让当前节点的左指针指向前驱
        root->ltag = 1;
    }
    if (*prev != NULL && (*prev)->right == NULL)
    {
        (*prev)->right = root;//让前一个指针的右节点指向它的后继
        (*prev)->rtag = 1;
    }
    *prev = root;
}

void PreCreateT(TreeNode*root,TreeNode**prev)
{
    if (root == NULL)
        return;
    Visit(root,prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
    //所以左边要判断
    if (root->ltag == 0)
        PreCreateT(root->left, prev);
    //当该节点的父节点的右孩子为空时,就会被改变指向它的后继,也就是当前节点,很显然会形成环,要判断
    if (root->rtag == 0)
        PreCreateT(root->right, prev);
}

void PreThread(TreeNode* root)
{
    if (root)
    {
        TreeNode* prev = NULL;
        PreCreateT(root,&prev);
        if (prev->right == NULL)
            prev->rtag = 1;
    }
}

后序代码如下


void Visit(TreeNode* root,TreeNode**prev)
{
    //root代表当前节点,*prev代表遍历root之前的一个节点
    //指针指向前驱和后继的条件是为空
    if (root->left == NULL)
    {
        root->left = *prev;//让当前节点的左指针指向前驱
        root->ltag = 1;
    }
    if (*prev != NULL && (*prev)->right == NULL)
    {
        (*prev)->right = root;//让前一个指针的右节点指向它的后继
        (*prev)->rtag = 1;
    }
    *prev = root;
}

void PostCreateT(TreeNode* root, TreeNode** prev)
{
    if (root == NULL)
        return;
    //if (root->ltag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
    PostCreateT(root->left, prev);
    //if (root->rtag == 0)//可加可不加---因为我们在改变完root之前就已经遍历过它了
    PostCreateT(root->right, prev);
    Visit(root, prev);//只能改变root之前的节点的右指针,和root的左指针,不会改变root的右指针
}

void PostThread(TreeNode* root)
{
    if (root)
    {
        TreeNode* prev = NULL;
        PostCreateT(root, &prev);
        if (prev->right == NULL)
            prev->rtag = 1;
    }
}

它们之间的区别其实只是先序遍历,中序遍历,后序遍历之间的区别,其他的代码几乎一摸一样(如果分不清楚什么时候要判断ltag和rtag,就全部判断就行)

附加用先序创建二叉线索树:

TreeNode* BuyTNode(TDataType x)
{
    TreeNode* newnode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        return NULL;
    }
    newnode->val = x;
    newnode->ltag = newnode->rtag = 0;
    newnode->left = newnode->right = NULL;
    return newnode;
}

//用先序遍历创建二叉树
void CreateT(TreeNode** root)
{
    TDataType x;
    scanf("%d", &x);
    if (x == 0)//这里假设0代表空节点,没有啥实际意义,跟根据具体情况修改
        return;
    else
    {
        *root = BuyTNode(x);
        CreateT(&((*root)->left));
        CreateT(&((*root)->right));
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值