【C语言】线索二叉树 实现先序创建+中序线索化+递归中序遍历+非递归中序遍历

github仓库地址 自取 by preciouswxe/HDU-data_structure

各位大佬不吝star⭐鸣谢

理论

可参考线索二叉树 先序-中序-后续 图解

重点是怎么利用指针域,搞清楚LTag和RTag是什么,以及整个线索树怎么完整连接起来。这个作业有些特殊的要求,但是就不放上来了。

实现内容

  1. InitBiThrTree 函数:

按照先序遍历的顺序来创建二叉树。它从标准输入读取字符序列,其中 # 表示空节点。通过递归方式,根据输入字符依次为二叉树分配节点内存,初始化节点的数据域以及 LTag 和 RTag(初始化为普通子树指针的标记 0),并继续递归创建左子树和右子树节点,最终构建出完整的二叉树结构。

输入输出:接收一个指向 BiThrTree 类型指针的指针作为参数,用于返回创建好的二叉树的根节点地址;返回值为 Status 类型,表示创建操作是否成功(成功返回 OK,失败返回相应错误码)。

  1. InOrderThreading 函数:

实现对给定二叉树的中序线索化操作。首先创建一个头结点,其 data 域设为 @,左指针指向二叉树的根节点(若二叉树非空),右指针先预留用于后续指向中序遍历的最后一个节点。接着利用一个栈辅助进行非递归的中序遍历,在遍历过程中,对于没有左子树的节点设置其左线索指向当前的前驱节点(通过 pre 指针记录),对于前驱节点中没有右子树的情况,将其右指针设置为当前节点作为后继线索。遍历结束后,把头结点与中序遍历的最后一个节点通过线索关联起来,完善整个中序线索二叉树的结构。

输入输出:接收两个 BiThrTree 类型的指针作为参数,第一个指针用于返回中序线索二叉树的头结点地址,第二个指针指向要进行线索化的原始二叉树的根节点;返回值表示线索化操作是否成功(成功返回 OK)。

  1. InOrderTraverse 函数:

以递归的方式对二叉树(可以是普通二叉树也可以是线索二叉树)进行中序遍历,并按照特定格式输出每个节点的相关信息,包括 LTag 值、左指针所指元素(若为线索则输出 ^,若左子树为空输出 NULL)、本节点的值、右指针所指元素以及 RTag 值等,清晰展示节点间的关系和二叉树结构特点。

输入输出:接收一个 BiThrTree 类型的指针指向要遍历的二叉树的根节点,无返回值,直接在函数执行过程中进行输出操作。

  1. InOrderTraverse_Thr 函数:

实现非递归方式对中序线索二叉树的遍历操作。先通过循环找到最左边的左节点作为起始遍历点,然后进入循环,依据节点的线索标记和子树指针情况依次输出每个节点的详细信息,包括线索标记、左右子树相关内容以及节点值等。在移动到下一个节点时,根据当前节点的右线索标记(RTag)来决定是沿着普通右子树找最左节点还是顺着线索直接跳转,直到遍历完整个中序线索二叉树(通过判断节点数据是否为头结点的特殊标记 @ 来确定结束)。

输入输出:接收一个 BiThrTree 类型的指针指向中序线索二叉树(通常是头结点的左子树,即实际二叉树部分),无返回值,在遍历过程中输出各节点信息。

  1. 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)

相关推荐
不爱学英文的码字机器5 分钟前
Rust 的征服:从系统编程到全栈开发的 IT 新宠
开发语言·后端·rust
瀚海澜生14 分钟前
链表系列入门指南(二):吃透这几题,链表解题不再难
后端·算法
爱编码的傅同学18 分钟前
数据结构(五)——AVL树(平衡二叉搜索树)
数据结构·算法
Bonnie_121519 分钟前
02-redis-数据结构实现原理
数据结构·redis·算法
Wood_Like40 分钟前
从递归入手一维动态规划
算法·动态规划
折枝寄北44 分钟前
数据结构 | 证明链表环结构是否存在
数据结构·链表
LAOLONG-C1 小时前
先占个日常,等会写。
数据结构
q567315231 小时前
用Dispatch库的爬虫程序爬取图片网站
开发语言·爬虫·python·scrapy
knightkkzboy1 小时前
《C语言中的“魔法盒子”:自定义函数的奇妙之旅》
c语言·开发语言·函数
Jelena技术达人2 小时前
深入解析:Python 爬取淘宝商品上下架接口
开发语言·python