线索二叉树的建立和遍历

留个笔记

Description

建立中序线索二叉树,并按中序遍历该二叉树。

Input

输入数据有多组,对于每组测试数据
按先序遍历顺序输入二叉树的各个结点值,#表示空节点。

Output

该二叉树的中序遍历序列。

Sample Input

ABD##E##C##

Sample Output

DBEAC

感觉有点鸡肋

前置定义

char a[10000];//读取字符串
int index;//用作建树的时候控制数组下表,这里有多组样例,也不知道有什么更好的办法
typedef struct TreeNode* ptr;
#define Size sizeof(TreeNode)
struct TreeNode {
	char data;
	int ltag,rtag;//标记是线索还是指针
	ptr lchild, rchild;//
};

建立线索二叉树首先要建立一棵普通的二叉树,这里给出了先序遍历的序列就直接建了,就先把所有指针域的tag赋值为1了,后面还要修改的

//建立二叉树
void createTree(ptr &p) {
	if (index == strlen(a))return;//建完了
	index++;
	char ch = a[index];
	if (a[index] == '#') {
		p = NULL;
		return;
	}
	p = (ptr)malloc(Size);
	p->data = ch; //不要忘记填充数据
	p->ltag = 1;
	p->rtag = 1;//都赋值为指针
	createTree(p->lchild);
	createTree(p->rchild);
}

接下来是线索二叉树的建立

就以中序序列为例,这里把0作为线索,1作为指针,左线索指向前驱,有线索指向后继也就是ltag和rtag

为了后面的遍历的方便一般会额外建立一个根节点的根节点,也就是所谓头结点,作为遍历序列的前驱和最后一个节点的后继,也就是最先和最后

第一步是进行头结点的初始化,头结点的两个指针域一定是线索,一开始让头结点的右线索指向自己,因为这时候还不知道后继是谁,左线索要视根节点的情况而定,要是根节点为空也就是一棵空树的话,没有人的前驱是头结点,那么头结点的左线索也指向自己。

然后递归建立线索二叉树,这里的pre是全局变量,在初始化的时候pre指向了头结点,传参是根节点,可以看到这里的pre和p(传参)就是前驱和后继的关系,在后面的动态变化中也要一直维护这种关系(这也是为什么一定要以中序去建立线索二叉树),从而能够不断去更新pre节点的右线索(后继)和p的左线索(前驱),当然更新的前提是这个指针域必须为空。

ptr pre;//全局变量用来记录前驱用的,这里实际上一直在变

//线索化
void thread(ptr p) {//地址不引用也可以修改内容
	if (p == NULL)return;//空节点无意义
	thread(p->lchild);
	//下面开始线索化,右线索指向后继,左线索指向前驱
	if (pre->rchild == NULL) {
		pre->rtag = 0;
		pre->rchild = p;
	}
	if (p->lchild == NULL) {
		p->ltag = 0;
		p->lchild = pre;
	}
	pre = p;
	thread(p->rchild);
}

//建立线索二叉树
//线索0指针1
void createThread(ptr &pf,ptr pb) {//pfront和pbehind
//分别前驱和当前节点,其实就是头结点和根节点,这里实际上到最后都是一样的
	pf = (ptr)malloc(Size);
	pf->ltag = 0;
	pf->rtag = 0;//线索
	pf->rchild = pf;//右指针指向自己
	if (!pb) {
		pf->lchild = pf;
		return;
	}//空树
	pre = pf;
	pf->lchild = pb;
	thread(pb);
	pre->rtag = 0;
	pre->rchild = pf;//pf没有变但是pre变了,变成了最后一个节点,这个顺序要注意
}

遍历操作

线索二叉树本身的右线索就已经指向了遍历序列的后继,只要找到了右线索,那么指向的那个节点直接输出就可以,所以首先要根据一定的顺序找到第一个节点,中序的第一个节点就是顺着头结点一直向左下找,找到左孩子为空(有右孩子不影响,根在右之前),那么这个就是第一个节点,这个其实是一组输出,然后根据左根右遍历原则,这时候去看右指针域是不是线索,是的话直接跳转输出,不是的话(可能是有右孩子,这时候tag为指针,或者已经指向头结点了),那么就退出,跳转到那个指针,然后再以相同的规则,一直往左下找找到没有右孩子,再去找一直找到最终指向头结点退出

//其实就是按照规则先去找到最左下的然后通过右线索(直接指向遍历序列的后继),再以同样规则查找
void print(ptr T) {
	ptr p;
	p = T->lchild;
	//开始遍历
	while (p != T) {
		while (p->ltag == 1)p = p->lchild;//找到最左的节点
		printf("%c", p->data);//输出最左的字母
		while (p->rtag == 0 && p->rchild != T) {//找后继,一直找找到找不到了为止
			p = p->rchild;
			printf("%c", p->data);
		}
		p = p->rchild;
	}
}

全部代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

char a[10000];
int index;//用作建树的时候控制数组下表,这里有多组样例,也不知道有什么更好的办法
typedef struct TreeNode* ptr;
#define Size sizeof(TreeNode)
struct TreeNode {
	char data;
	int ltag,rtag;//标记是线索还是指针
	ptr lchild, rchild;//
};

//建立二叉树
void createTree(ptr &p) {
	if (index == strlen(a))return;//建完了
	index++;
	char ch = a[index];
	if (a[index] == '#') {
		p = NULL;
		return;
	}
	p = (ptr)malloc(Size);
	p->data = ch; //不要忘记填充数据
	p->ltag = 1;
	p->rtag = 1;//都赋值为指针
	createTree(p->lchild);
	createTree(p->rchild);
}

ptr pre;//全局变量用来记录前驱用的,这里实际上一直在变

//线索化
void thread(ptr p) {//地址不引用也可以修改内容
	if (p == NULL)return;//空节点无意义
	thread(p->lchild);
	//下面开始线索化,右线索指向后继,左线索指向前驱
	if (pre->rchild == NULL) {
		pre->rtag = 0;
		pre->rchild = p;
	}
	if (p->lchild == NULL) {
		p->ltag = 0;
		p->lchild = pre;
	}
	pre = p;
	thread(p->rchild);
}

//建立线索二叉树
//线索0指针1
void createThread(ptr &pf,ptr pb) {//分别前驱和当前
	pf = (ptr)malloc(Size);
	pf->ltag = 0;
	pf->rtag = 0;//线索
	pf->rchild = pf;//右指针指向自己
	if (!pb) {
		pf->lchild = pf;
		return;
	}//空树
	pre = pf;
	pf->lchild = pb;
	pf->ltag = 0;//线索
	thread(pb);
	pre->rtag = 0;
	pre->rchild = pf;//pf没有变但是pre变了,变成了最后一个节点,这个顺序要注意
}

//其实就是按照规则先去找到最左下的然后通过右线索(直接指向遍历序列的后继),再以同样规则查找
void print(ptr T) {
	ptr p;
	p = T->lchild;
	//开始遍历
	while (p != T) {
		while (p->ltag == 1)p = p->lchild;//找到最左的节点
		printf("%c", p->data);//输出最左的字母
		while (p->rtag == 0 && p->rchild != T) {//找后继,一直找找到找不到了为止
			p = p->rchild;
			printf("%c", p->data);
		}
		p = p->rchild;
	}
}

void mediumtPrint(ptr p) {
	if (p == NULL)return;
	mediumtPrint(p->lchild);
	printf("%c", p->data);
	mediumtPrint(p->rchild);
}

int main()
{
	ptr root, rootParent;
	while (~scanf("%s", a)){
		index = -1;
		root = (ptr)malloc(Size);
		createTree(root);//先建树
		//mediumtPrint(root);//验证树建立的正确性
		createThread(rootParent, root);//建立线索二叉树
		print(rootParent);//中序遍历线索二叉树输出
		printf("\n");
	}
	return 0;
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值