数据结构 实验16:中序线索二叉树的基本操作

目录

前言

实验要求:

代码实现和思路讲解

线索二叉树的定义:

先序创建二叉树:

创建带头结点的中序线索化二叉树:

找前驱和找后继两种方式遍历线索化中序二叉树:

结果测试

中序线索二叉树图:

 测试结果:

总结


前言

建立线索二叉树的思路差不多,这次主要是针对中序线索二叉树的基本操作,既可以找前驱,又可以找后继,最具有代表性。之后我会单独做关于树一章节的总结。

实验要求:

1.先序遍历创建二叉树;

2.中序线索化二叉树;

3.分别通过 找前驱 和 找后继 的方式,遍历中序线索二叉树。

代码实现和思路讲解

线索二叉树的定义:

我这里为了更加直观,将标志单独用枚举型独立出来了。自定义命名isClue:clue = 1,表示这个位置的指针是线索指针,child = 0,也有否的含义,表示不是线索指针,那就是孩子指针了。

typedef enum { clue = 1, child = 0 } isClue; 	//1:是线索,0:是孩子 

typedef char Elemtype;

typedef struct btNode {
	Elemtype data;
	isClue lTag, rTag;
	struct btNode *lChild;
	struct btNode *rChild;
} TBTNode;

先序创建二叉树:

这个非常简单

TBTNode *PreCreateBTree(char *str, int *loca) {
    TBTNode *T;
    if (str[*loca] == '#') { 	//如果当前字符是'#',表示空树
        (*loca)++; 				//移动到下一个字符
        return NULL; 			//返回空指针
    }
    else { 						//否则,表示有数据
        T = (TBTNode *)malloc(sizeof(TBTNode)); 
        if (!T) { 
            exit(0);
        }
        T->data = str[(*loca)++];
        T->lChild = PreCreateBTree(str, loca); //递归创建左子树
        T->rChild = PreCreateBTree(str, loca); //递归创建右子树
        return T; //返回根结点指针
    }
}

创建带头结点的中序线索化二叉树:

分为两个步骤:一、创建头结点完善线索化的二叉链表。二、将原本的二叉链表线索化。

中序线索化,我们自然是用中序遍历,直接格式:递归左子树,访问根节点,递归右子树。于是我们只需要对一个结点负责,如果左子树存在,标志为child,不存在就线索化。全局变量pre指向遍历过程中的前驱结点,在线索化二叉链表之前已经初始化为头结点了。

如果二叉树是空树,头结点就头尾都指向自己。否则就左标志为child,链接根节点,右标志为clue链接最右,即中序遍历最后一个元素。

//中序线索化二叉树
TBTNode *pre = NULL; //定义一个全局变量pre
void InThread(TBTNode *T) {
    if (T) {
        InThread(T->lChild); 		//递归遍历左子树
        if (!T->lChild) {
            T->lTag = clue; 		//将lTag设为clue
            T->lChild = pre; 		//将lChild指向pre
        }
        else T->lTag = child;		//左孩子存在 
        if (!pre->rChild) {
            pre->rTag = clue; 		//将pre的rTag设为clue
            pre->rChild = T; 		//将pre的rChild指向当前结点
        }
        else pre->rTag = child;
        pre = T; 					//更新pre为当前结点
        InThread(T->rChild); 		//递归遍历右子树
    }
}

//创建中序线索化二叉树
TBTNode *CreateInThread(TBTNode *T) {
    pre = NULL;
    TBTNode *head = (TBTNode *)malloc(sizeof(TBTNode));
    if (!head) {
        exit(0);
    }
    head->lTag = child; 		//头结点的lTag为chile
    head->rTag = clue; 			//头结点的rTag为clue
    head->rChild = head; 		//头结点的rChild指向自己
    if (!T) { 					//如果二叉树为空
        head->lChild = head; 	//头结点的lChild也指向自己
    }
    else {
        head->lChild = T; 		//头结点的lChild指向原来的根结点
        pre = head;
        InThread(T); 			//中序线索化二叉树
        pre->rChild = head; 	//处理最后一个结点,将其rChild设为头结点
        pre->rTag = clue; 		//将其rTag设为clue
        head->rChild = pre; 	//将头结点的rChild指向最后一个结点
    }
    return head;
}

找前驱和找后继两种方式遍历线索化中序二叉树:

中序遍历的结点前驱是左子树的最右结点,后继是右子树的最左结点。

找前驱开始结点为最右结点,因为是最后一个中序遍历结点。可以通过头结点的右指针快速访问到并输出。然后就是一直找前驱,左指针是线索指针可以直接访问,如果不是就找左孩子的最右子树。

找后继则要先访问根节点,通过循环找到最左子树,因为是第一个中序遍历结点。然后就是和上面思路一样,一直找后继,右线索指针就直接访问,没有就找右子树的最左子树。

所以两种遍历方法的代码几乎一模一样:

//找前驱的方式遍历中序线索二叉树
void InOrderPre(TBTNode *head) {
    TBTNode *p = head->rChild;	//指向最右子树 
    while (p != head) {
        while (!p->rTag) {		//右指针不是线索,即是孩子 
            p = p->rChild;		//左子树最右孩子是前驱 
        }
        printf("%c ", p->data);
        while (p->lTag && p->lChild != head) {	//左指针是线索且不是头结点 
            p = p->lChild;
            printf("%c ", p->data);
        }
        p = p->lChild;
    }
}

// 找后继的方式遍历中序线索二叉树
void InOrderNext(TBTNode *head) {
    TBTNode *p = head->lChild;
    while (p != head) {
        while (!p->lTag) {		//左指针不是线索,即是孩子 
            p = p->lChild;		//右子树最左孩子 
        }
        printf("%c ", p->data);
        while (p->rTag && p->rChild != head) {	//右指针是线索且不是头结点 
            p = p->rChild;
            printf("%c ", p->data);
        }
        p = p->rChild;
    }
}

结果测试

中序线索二叉树图:

红线为后继,黄线为前驱:

 测试结果:

总结

通过找前驱的方式遍历中序线索二叉树,结果会和中序遍历相反,可以通过栈等方式逆序一下就可以了。

主要需要明确思路,找后继遍历就得找到第一个元素,最左元素,然后依次找后继,有线索找线索,没线索找右子树的最左端。找前驱就得找最后一个元素,最右元素,然后依次找前驱,有线索访问线索,没有线索访问左子树的最右子树。

线索化过程没有什么难度,就是中序遍历,将中间的print方法替换成我们想要的方法,需要注意的就是用一个pre指针,备份访问过的前驱结点,最后注意将头结点的线索化补充完整。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Leisure_水中鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值