github仓库地址 自取 by preciouswxe/HDU-data_structure
各位大佬不吝star⭐鸣谢
理论
重点是怎么利用指针域,搞清楚LTag和RTag是什么,以及整个线索树怎么完整连接起来。这个作业有些特殊的要求,但是就不放上来了。
实现内容
- InitBiThrTree 函数:
按照先序遍历的顺序来创建二叉树。它从标准输入读取字符序列,其中 # 表示空节点。通过递归方式,根据输入字符依次为二叉树分配节点内存,初始化节点的数据域以及 LTag 和 RTag(初始化为普通子树指针的标记 0),并继续递归创建左子树和右子树节点,最终构建出完整的二叉树结构。
输入输出:接收一个指向 BiThrTree 类型指针的指针作为参数,用于返回创建好的二叉树的根节点地址;返回值为 Status 类型,表示创建操作是否成功(成功返回 OK,失败返回相应错误码)。
- InOrderThreading 函数:
实现对给定二叉树的中序线索化操作。首先创建一个头结点,其 data 域设为 @,左指针指向二叉树的根节点(若二叉树非空),右指针先预留用于后续指向中序遍历的最后一个节点。接着利用一个栈辅助进行非递归的中序遍历,在遍历过程中,对于没有左子树的节点设置其左线索指向当前的前驱节点(通过 pre 指针记录),对于前驱节点中没有右子树的情况,将其右指针设置为当前节点作为后继线索。遍历结束后,把头结点与中序遍历的最后一个节点通过线索关联起来,完善整个中序线索二叉树的结构。
输入输出:接收两个 BiThrTree 类型的指针作为参数,第一个指针用于返回中序线索二叉树的头结点地址,第二个指针指向要进行线索化的原始二叉树的根节点;返回值表示线索化操作是否成功(成功返回 OK)。
- InOrderTraverse 函数:
以递归的方式对二叉树(可以是普通二叉树也可以是线索二叉树)进行中序遍历,并按照特定格式输出每个节点的相关信息,包括 LTag 值、左指针所指元素(若为线索则输出 ^,若左子树为空输出 NULL)、本节点的值、右指针所指元素以及 RTag 值等,清晰展示节点间的关系和二叉树结构特点。
输入输出:接收一个 BiThrTree 类型的指针指向要遍历的二叉树的根节点,无返回值,直接在函数执行过程中进行输出操作。
- InOrderTraverse_Thr 函数:
实现非递归方式对中序线索二叉树的遍历操作。先通过循环找到最左边的左节点作为起始遍历点,然后进入循环,依据节点的线索标记和子树指针情况依次输出每个节点的详细信息,包括线索标记、左右子树相关内容以及节点值等。在移动到下一个节点时,根据当前节点的右线索标记(RTag)来决定是沿着普通右子树找最左节点还是顺着线索直接跳转,直到遍历完整个中序线索二叉树(通过判断节点数据是否为头结点的特殊标记 @ 来确定结束)。
输入输出:接收一个 BiThrTree 类型的指针指向中序线索二叉树(通常是头结点的左子树,即实际二叉树部分),无返回值,在遍历过程中输出各节点信息。
- main 函数:
作为程序的入口点,按照顺序调用上述各个函数来完成完整的操作流程。首先提示用户输入先序遍历的字符序列以创建二叉树,然后分别展示未线索化二叉树的中序遍历结果、进行中序线索化操作后,再依次输出中序线索二叉树的递归和非递归遍历结果,直观呈现二叉树在不同阶段的结构和遍历情况。
完整代码
懒得分了,直接上全部代码,看注释
c
//
// Created by 13561 on 2024/11/28.
//
#include<stdio.h>
#include<stdlib.h>
#define ERROR 0
#define TRUE 1
#define OK 1
#define MAXSIZE 100
typedef int Status; //声明函数类型名
typedef char TElemType; //声明结点元素值得类型
typedef struct BiThrNode { //定义线索二叉链表的结点结构和类型
TElemType data;
struct BiThrNode *lchild, *rchild;
int LTag, RTag;
}BiThrNode,*BiThrTree;
// 先序进行二叉树创建
Status InitBiThrTree(BiThrTree *T) {
TElemType ch;
scanf(" %c",&ch);
if(ch == '#') {
*T = NULL;
} else {
*T = (BiThrNode *)malloc(sizeof(BiThrNode));
if((*T) == NULL) {
return -1;
}
(*T)->data = ch;
(*T)->LTag = 0;
(*T)->RTag = 0;
// 初始化左孩子
InitBiThrTree(&(*T)->lchild);
// 初始化右孩子
InitBiThrTree(&(*T)->rchild);
}
return OK;
}
// 按中序遍历进行线索化
Status InOrderThreading(BiThrTree *Thrt, BiThrTree T) {
// pre 是一个辅助指针变量 用于在中序遍历线索化的过程中记录当前节点的前驱结点
BiThrTree pre;
// *Thrt 所指向的是中序线索二叉树的头结点
// 头结点的data域存放字符'@',指针Thrt指向该头结点
// 头结点的左指针指向二叉树的根结点(LTag=0),右指针指向该二叉树中序遍历的最后一个结点(RTag=1)
*Thrt = (BiThrNode *)malloc(sizeof(BiThrNode));
(*Thrt)->data = '@';
(*Thrt)->LTag = 0;
(*Thrt)->RTag = 1;
if (!T) {
// 处理空树情况
(*Thrt)->lchild = *Thrt;
} else {
// T 是传入的原始二叉树的根结点指针。从头结点Thrt出发,通过左指针找到根结点
(*Thrt)->lchild = T;
// pre变成头结点
pre = *Thrt;
// 开始非递归中序遍历进行线索化,也就是借助栈
BiThrTree p = T;
BiThrTree Stack[100];
int top = -1;
while(p || top!= -1) {
// 找到这一轮最左的结点
while(p) {
Stack[++top] = p;
p = p->lchild;
}
if(top!=-1) {
// 出栈
p = Stack[top--];
// 没有左孩子就写1和前驱
if(!p->lchild) {
printf("p!\n");
p->LTag = 1;
p->lchild = pre;
printf("P node: %c, LTag: %d, RTag: %d,lchild:%p,rchild:%p \n\n", p->data, p->LTag, p->RTag,p->lchild,p->rchild);
}
// 前驱节点 pre 的右子树为空,将 pre 的右指针指向当前节点 p。因为pre本来就是p的前驱
if(!pre->rchild) {
printf("pre!\n");
pre->RTag = 1;
pre->rchild = p;
printf("Pre node: %c, LTag: %d, RTag: %d,lchild:%p,rchild:%p\n\n", pre->data, pre->LTag, pre->RTag,pre->lchild,pre->rchild);
}
// 更新 pre 为当前节点 p,以便在下一次循环中正确记录新的前驱节点
pre = p;
// 往右边找
p = p->rchild;
}
}
// 将最后一个节点(此时 pre 指向最后一个节点)的右指针指向头结点 *Thrt,使头结点的右指针指向中序遍历的最后一个节点
pre->rchild = *Thrt;
pre->RTag = 1;
(*Thrt)->rchild = pre;
printf("LAST node: %c, LTag: %d, RTag: %d,lchild:%p,rchild:%p\n\n", pre->data, pre->LTag, pre->RTag,pre->lchild,pre->rchild);
printf("Thrt node: %c, LTag: %d, RTag: %d,lchild:%p,rchild:%p\n\n", (*Thrt)->data, (*Thrt)->LTag, (*Thrt)->RTag,(*Thrt)->lchild,(*Thrt)->rchild);
}
return OK;
}
// 按孩子中序遍历
// 打印 | LTag | 左指针所指元素 | 本结点的值 | 右指针所指元素 | RTag |
Status InOrderTraverse(BiThrTree T) {
if (T) {
// 递归地遍历二叉树 T 的左子树,会一直深入到最左边的叶子节点,然后逐步向上返回
printf("Current node: %c, lchild pointer: %p,rchild pointer: %p\n", T->data, T->lchild,T->rchild);
// 警告!!如果没有这个判断条件则会陷入死循环!!因为会导致一直找lchild然后反复回到根节点
if(T->LTag == 0) {
InOrderTraverse(T->lchild);
}
// 输出当前结点信息
printf("| %d |",T->LTag);
if(T->LTag == 1) {
printf("^ | ");
} else if (T->lchild != NULL) {
printf("%c |",T->lchild->data);
} else {
printf("NULL |");
}
printf("%c | ",T->data);
if(T->RTag == 1) {
printf("^ | ");
}else if (T->rchild!=NULL){
printf("%c | ",T->rchild->data);
}else {
printf("NULL |");
}
printf("%d |\n",T->RTag);
// 递归遍历右子树 这里同理!
if(T->RTag == 0) {
InOrderTraverse(T->rchild);
}
}
return OK;
}
// 按线索和孩子遍历(非递归,格式同上)
Status InOrderTraverse_Thr(BiThrTree T) {
BiThrTree p = T;
// 先找有最左边的左结点的根结点
while (p->LTag == 0) {
p = p->lchild;
}
// 根据是不是遍历到头结点来判断是否循环完毕
while (p->data!='@') {
// 左
printf("| %d | ", p->LTag);
if (p->LTag == 1) {
printf("^ | ");
} else {
printf("%c | ", p->lchild->data);
}
// 值 注意这时候输出的是根节点的值 因为必须是有左子树的p才进入这个循环
printf("%c | ", p->data);
// 右
if (p->RTag == 1) {
printf("^ | ");
} else {
printf("%c | ", p->rchild->data);
}
printf("%d |\n", p->RTag);
// 往后找 如果有右孩子就遍历看看有没有左子树
if (p->RTag == 0) {
p = p->rchild;
while (p->LTag == 0) {
p = p->lchild;
}
} else {
p = p->rchild;
}
}
// 把头结点单独再输出一下 ,可以省略因为@不是二叉树的内容
printf("| %d | ", p->LTag);
if (p->LTag == 1) {
printf("^ | ");
} else {
printf("%c | ", p->lchild->data);
}
// 值 注意这时候输出的是根节点的值 因为必须是有左子树的p才进入这个循环
printf("%c | ", p->data);
// 右
if (p->RTag == 1) {
printf("^ | ");
} else {
printf("%c | ", p->rchild->data);
}
printf("%d |\n", p->RTag);
return OK;
}
int main() {
BiThrTree T,Thrt;
// (1)调用InitBiThrTree(T)函数,创建一棵按线索二叉链表结构存储的尚未线索化的二叉树
printf("请输入先序遍历的字符序列(#表示空节点):\n");
InitBiThrTree(&T);
printf("T初始化完成。\n");
printf("-------------------------------\n");
// (2)调用InOrderTraverse(T)函数,输出每个结点的数据
printf("未线索化二叉树按孩子中序遍历结果:\n");
InOrderTraverse(T);
printf("-------------------------------\n");
// (3)调用InOrderThreading(Thrt, T)函数,将T线索化成一棵中序线索二叉树
InOrderThreading(&Thrt, T);
printf("Thrt线索化完成。\n");
printf("-------------------------------\n");
// (4)调用InOrderTraverse(Thrt)函数,输出每个结点的数据
printf("中序线索二叉树(递归遍历)结果:\n");
InOrderTraverse(Thrt->lchild); //Thrt是头结点 指向根结点
printf("-------------------------------\n");
// (5)调用InOrderTraverse_Thr(Thrt)函数,输出每个结点的数据
printf("中序线索二叉树(非递归遍历)结果:\n");
InOrderTraverse_Thr(Thrt->lchild);
}
开源by:祁许 (freedomwxe)