线索二叉树

二叉树链式存储的缺点就是有很多指向空的指针,这样会浪费很多空间。

一棵有n个结点的二叉树,共有2n个指针域,有n-1个分支线,就有2n-(n-1) = n + 1的空指针域。

每个空指针都应该被利用起来,可以让它们指向遍历时其前一个结点或者后一个结点,称遍历时的前一个结点为当前结点的前驱, 遍历时的后一个结点为这个结点的后继。

比如下面的一棵树,中序遍历为:HDIBJEAFCG

对于I结点来说,前驱就是D,后继就是B,原来的二叉树I的左右结点都没有用到。现在可以让它记录一下其前驱和后继,一般是左前驱,右后继。

在这里插入图片描述

线索的实现:

/*
    功能:中序遍历进行中序线索化(最左结点的左孩子,最右结点的右孩子指向NULL,且最右结点没有线索化)
    参数:根结点指针
    注:左树空接前驱, 右树空接后继。
    前驱和后继是相对于中序遍历的结果。
    先打印的是前驱,后打印的是后继。
*/
BiThrTree pre = NULL; //全局变量,用于指向前一个访问的结点
void InThreading(BiThrTree p)
{
    if (p)
    {
        InThreading(p->lchild); //递归左子树线索化
        if (p->lchild == NULL) //如果左子树为空
        {
            p->LTag = Thread; //前驱线索
            p->lchild = pre;  //左指针指向前驱,最开始是空
        }
        if (pre != NULL && pre->rchild == NULL)//前驱不为空且没有右孩子
        {
            pre->RTag = Thread; //后继线索
            pre->rchild = p; //前驱右孩子指向后继
        }
        pre = p; //保证pre是p的前驱,也就是p是pre的后继
        InThreading(p->rchild);//递归右子树线索化
    }
}

这样线索完了后,如果再加一个头结点,其左孩子指向根,右孩子指向最后继(G结点)。把头结点作为最前驱,同时也作为最后继(G结点)的后继。

可以通过左树是否是Thread找到中序遍历的第一个结点(H结点),然后根据右线索找到后面的结点。如果结点没有右线索怎么办?(比如B结点)那就表明当前结点有右树(E为根的树),再去用同样的方法(左树是否是Thread)来找到它的第一个结点(J)

在这里插入图片描述

二叉线索树遍历方法:

/*
    功能:中序遍历二叉线索树
    参数:头结点指针
*/
void InOrderTraverse(BiThrTree head)
{
    BiThrTree p = head->lchild;//p指向根结点
    while(p != head)//没有回到头结点,也就是没有遍历完
    {
        while(p->LTag == Link)//找到左孩子是线索的结点,这个结点没有左孩子,根据中序遍历规则,它就是第一个结点
            p = p->lchild;
        printf("%c", p->data);//输出p为根的第一个结点
        while(p->RTag == Thread && p->rchild != head)//如果右孩子有线索且不为头结点,根据线索找后面的结点
        {
            p = p->rchild;
            printf("%c", p->data);
        }
        //右孩子不是线索,就去右孩子,把右孩子当作一棵树来遍历
        p = p->rchild;//访问右孩子,p作为右树的根
    }
}

可以看出用LTag找到树(也包括子树)的第一个结点,用RTag来找后面线索结点。把一对二的关系变成了一对一的关系。

对于需要经常遍历的操作,线索二叉树的时间复杂度0(n)会比用递归方法降低很多。

二叉线索树释放内存:

/*
    功能:释放树
    参数:树的头结点指针
*/
void FreeBiThrTree(BiThrTree T)
{
    BiThrTree p = T->lchild;
    BiThrTree q = p; // 用于存要释放的结点
    while(p != T)
    {
        while(p->LTag == Link)//找到第一个结点
        {
            p = p->lchild;
        }
        //如果有右孩子有线索,且不为头结点,那可以根据线索找后面的结点, 找到一个释放一个
        while(p->RTag == Thread && p->rchild != T)
        {
            q = p;//存住要释放结点内存
            p = p->rchild;
            free(q);//释放
        }
        //右孩子没有线索,根据中序遍历规则,去遍历右树,p指向右孩子作为右树的根结点再进行下一轮循环
        q = p;
        p = p->rchild;
        free(q);
    }
}

头文件head.h

#ifndef HEAD_H
#define HEAD_H

typedef enum{
    Link, //没有线索
    Thread //有线索
}PointerTag;

typedef char TElementType;
    
typedef struct Node* BiThrTree;

typedef struct Node{
    TElementType data;
    BiThrTree lchild;
    BiThrTree rchild;
/*标志有没有线索*/
    PointerTag LTag; 
    PointerTag RTag;
}BiThrNode;

BiThrTree pre; //全局变量,用于指向前一个访问的结点

/*中序遍历进行中序线索化*/
void InThreading(BiThrTree p);
/*建立二叉树*/
BiThrTree CreateBiThrTree(void);
/*释放内存*/
void FreeBiThrTree(BiThrTree T);
/*中序遍历*/
void InOrderTraverse(BiThrTree T);
/*添加头指针*/
BiThrTree ChangePoint(BiThrTree T);

#endif 

操作集operation.c

#include "head.h"
#include <stdio.h>
#include <stdlib.h>
/*
    功能:中序遍历进行中序线索化
    参数:根结点指针
    注:左树空接前驱, 右树空接后继。
    前驱和后继是相对于中序遍历的结果。
    先打印的是前驱,后打印的是后继。
*/
void InThreading(BiThrTree p)
{
    if (p)
    {
        InThreading(p->lchild); //递归左子树线索化
        if (p->lchild == NULL) //如果左子树为空
        {
            p->LTag = Thread; //前驱线索
            p->lchild = pre;  //左指针指向前驱,最开始是空
        }
        if (pre != NULL && pre->rchild == NULL)//前驱不为空且没有右孩子
        {
            pre->RTag = Thread; //后继线索
            pre->rchild = p; //前驱右孩子指向后继
        }
        pre = p; //保证pre是p的前驱,也就是p是pre的后继
        InThreading(p->rchild);//递归右子树线索化
    }
}
/*
    功能:建立一棵空二叉树
    返回:空树头指针
*/
BiThrTree CreateBiThrTree(void)
{
    BiThrTree T;
    TElementType ch;
    scanf("%c", &ch);
    if (ch == '#')
    {
        return NULL;
    }
    else
    {
        T = (BiThrTree)malloc(sizeof(BiThrNode));
        if (T == NULL)
        {
            printf("out of space!\n");
            exit(EXIT_FAILURE);
        }
        T->data = ch;
        T->LTag = Link;
        T->RTag = Link;
        T->lchild = CreateBiThrTree();
        T->rchild = CreateBiThrTree();
    }
    return T;
}
/*
    功能:释放树
    参数:树的头结点指针
*/
void FreeBiThrTree(BiThrTree T)
{
    BiThrTree p = T->lchild;
    BiThrTree q = p; // 用于存要释放的结点
    while(p != T)
    {
        while(p->LTag == Link)//找到第一个结点
        {
            p = p->lchild;
        }
        //如果有右孩子有线索,且不为头结点,那可以根据线索找后面的结点, 找到一个释放一个
        while(p->RTag == Thread && p->rchild != T)
        {
            q = p;//存住要释放结点内存
            p = p->rchild;
            free(q);//释放
        }
        //右孩子没有线索,根据中序遍历规则,去遍历右树,p指向右孩子作为右树的根结点再进行下一轮循环
        q = p;
        p = p->rchild;
        free(q);
    }
}
/*
    功能:中序遍历二叉线索树
    参数:头结点指针
*/

void InOrderTraverse(BiThrTree head)
{
    BiThrTree p = head->lchild;//p指向根结点
    while(p != head)//没有回到头结点,也就是没有遍历完
    {
        while(p->LTag == Link)//找到左孩子是线索的结点,这个结点其没有左孩子
        {
            p = p->lchild;
        }
        printf("%c", p->data);//输出这个结点
        while(p->RTag == Thread && p->rchild != head)//如果右孩子有线索且不为头结点,根据线索找后面的结点
        {
            p = p->rchild;
            printf("%c", p->data);
        }
        //右孩子不是线索,就去右孩子
        p = p->rchild;//访问右孩子,后继结点
    }
}
/*
    功能:添加头结点,将二叉树的空指针指向头结点
    参数:根结点指针,头结点指针的地址
*/
BiThrTree ChangePoint(BiThrTree T)
{

    if (T == NULL)
    {
        exit(EXIT_FAILURE);
    }
    BiThrTree head = (BiThrTree)malloc(sizeof(BiThrNode));
    BiThrTree record = T;
    while((record)->rchild)//找到最右结点,因为它是中序遍历的最后一个结点
    {   
        (record) = (record)->rchild;
    }   

    head->RTag = Thread;
    head->rchild = (record);//头结点右孩子指向最右结点

    (record)->RTag = Thread;
    (record)->rchild = head;//最右结点也指向头结点

    (record) = T; //record回到根结点
    while((record)->lchild)//找到最左结点
    {   
        (record) = (record)->lchild;
    }   

    head->LTag = Link;
    head->lchild = T;//头结点左孩子指向根结点

    (record)->LTag = Thread;
    (record)->lchild = head;//最左结点指向头结点
    return head;
}

主函数main.c

#include <stdio.h>
#include <stdlib.h>
#include "head.h"
#include "operation.c"

int main(void)
{
    BiThrTree T = CreateBiThrTree();
    InThreading(T);

    BiThrTree head = ChangePoint(T);

    InOrderTraverse(head);
    printf("\n");
    FreeBiThrTree(head);

    free(head);
    head = NULL;
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CCPigSnail

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值