中序线索二叉树的实现:代码和分析及运行结果

分析:


/*
* 用中序线索二叉树查找二叉树结点的中序前驱和中序后继
* (1)创建一个线索二叉树
* (2)对这个线索二叉树进行中序线索化 ====>> 变成一个中序线索二叉树
* (3)利用中序线索二叉树的线索查找该结点的中序前驱和中序后继

*/


/*
* 中序线索化(左根右):
* (a)如果一个结点没有左子树,那么这个结点的前驱肯定不是自己的左子树(NULL),而是按照中序遍历的顺序,这个结点的前驱肯定是在这个结点的前面一个遍历
*的结点(pre)---中序线索化中不管该节点有没有左子树,该节点的前驱肯定就是该节点的前面一个被中序遍历的结点。所以 T->lchild=pre ;
* (b)如果一个结点没有右子树,那么这个结点的后继肯定不是自己的右子树(NULL),而是按照中序遍历的舒徐,这个节点的后继肯定是中序遍历中
*该节点的后面一个被遍历到的,---中序线索化中不管该节点有没有右子树。当正在访问当前这个节点时,该节点的后继是肯定是还未被访问到的
*(后面一次的访问才是访问该节点的后继),
*所以,要连接上该节点的后继,应该时在下一个访问结点时,这时,正在访问的结点(T)肯定是之前结点(pre)的后继结点,
* 所以:当pre结点没有右子树时,pre结点的后继结点就是中序遍历的当前结点T,把当前结点T的地址赋给pre的后继指针:
*if(pre->lchild=NULL&&pre!=NULL)
* {pre->rchild=T;}
* (c)
* //其他的情况是有左孩子或有右孩子,
    //这些结点的中序前驱就是这些结点的左子树的最右下的结点;
    //这些结点的中序后继就是这些结点的右子树的最左下的结点

*/

代码:

#include <stdio.h>
#include <malloc.h>
#define MAX 10


//(1)创建一个中序线索二叉树

//定义线索二叉树结点
typedef struct BiThread 
{
	int data;
	int ltag;//该节点有左子树:0;没有左子树(NULL/已经线索化):1;//创建线索二叉树时,默认都为0 //在中序线索化该二叉树时,才会将左右子树为NULL的赋1
	int rtag;//该节点有右子树:0;没有右子树(NULL/已经线索化):1;//创建线索二叉树时,默认都为0
	struct BiThread* lchild;//该节点的左子树
	struct BiThread* rchild;//该节点的右子树

}BiTnode,* BiThread_Tree;



BiTnode* pre = NULL;//全局变量,指向线索二叉树当前正在访问(遍历)的结点T的 前一个已经被遍历的结点 
//(pre:用于记录当前正在遍历的结点的前一个已经被遍历过的结点的地址)//前一个足迹


/*要实现这个的话,还得再搞3个队列(用下面定义的数组的话是完成不了的),懒得搞,把你注释掉*/
//BiTnode* du_1_have_pre[MAX];//存储已经被中序线索化之后的ltag=1的结点地址,(可以直接访问前驱的结点)
//BiTnode* du_1_have_post[MAX];//存储已经被中序线索化之后的rtag=1的结点地址,(可以直接访问后继的结点)
//BiTnode* leaf[MAX];//存储已经被中序线索化之后的ltag=1&&rtag=1的结点地址,(可以直接访问前驱和后继的结点)


//初始化中序线索二叉树的根节点
BiThread_Tree Init_BiThread_root()
{
	int e;
	BiThread_Tree N = (BiThread_Tree)malloc(sizeof(BiTnode));
	printf("\n请输入该中序线索二叉树的根节点的值:");
	scanf_s("%d",&e);
	N->data = e;
	N->lchild = NULL;
	N->rchild = NULL;
	N->ltag = 0;
	N->rtag = 0;
	printf("\n根节点创建成功!");
	return N;
}

//为线索二叉树的结点T插入左孩子
void insert_lnode(BiTnode* &T)
{
	int flag = 1;
	printf("\n %d->lchild==NULL--1;%d->lchild==(int)--2:",T->data,T->data);
	scanf_s("%d",&flag);
	if (flag==2)
	{
		int e;
		BiTnode* child = (BiTnode*)malloc(sizeof(BiTnode));
		printf("\n请输入 %d->lchild->data: ", T->data);
		scanf_s("%d", &e);
		child->data = e;
		child->lchild = NULL;
		child->rchild = NULL;
		child->ltag = 0;
		child->rtag = 0;

		T->lchild = child;
	}
	else//T->lchild==NULL
	{
		//本来初始化每一个结点的时候就都设置为NULL的,所有这里就不需要操作了
	}

}

//为线索二叉树的结点T插入右孩子
void insert_rnode(BiTnode*& T)
{
	
	int flag = 1;
	printf("\n %d->rchild==NULL--1;%d->rchild==(int)--2:",T->data,T->data);
	scanf_s("%d", &flag);
	if (flag == 2)
	{
		int e;
		BiTnode* child = (BiTnode*)malloc(sizeof(BiTnode));
		printf("\n请输入 %d->rchild->data: ", T->data);
		scanf_s("%d", &e);
		child->data = e;
		child->lchild = NULL;
		child->rchild = NULL;
		child->ltag = 0;
		child->rtag = 0;

		T->rchild = child;
	}
	else//T->rchild==NULL
	{
		//本来初始化每一个结点的时候就都设置为NULL的,所有这里就不需要操作了
	}

}

//用递归实现创建一颗完整的线索二叉树(还未实现中序线索化) 
void creat_BiThread_Tree(BiThread_Tree &T)
{
	if (T!=NULL)
	{
		insert_lnode(T);//插入结点T的左孩子
		creat_BiThread_Tree(T->lchild);

		insert_rnode(T);//插入结点T的右孩子
		creat_BiThread_Tree(T->rchild);
	}
	
}



//(2)对这个线索二叉树进行中序线索化 ====>> 变成一个中序线索二叉树

//中序线索化是以中序遍历的顺序为基准,为已经创建好的线索二叉树 连接 中序线索(每个结点连接中序前驱和中序后继),
// 
//中序线索二叉树:可以直接找到该结点的中序前驱和中序后继的线索二叉树


//对线索二叉树进行中序线索化

void visit_BiTnode(BiThread_Tree T)
{
	printf("\n %p :%d",T,T->data);
	if (T->lchild == NULL)
	{
		T->lchild=pre;
		T->ltag = 1;//结点T的左孩子已经被中序线索化了
	}
	if (pre!=NULL&&pre->rchild==NULL)
	{
		pre->rchild=T;
		pre->rtag = 1;//结点T的右孩子已经被中序线索化了
	}

	pre=T;//每次访问完当前结点T,就把T的地址赋给pre,T指向下一个被中序遍历的结点

	//其他的情况是有左孩子或有右孩子,
	//这些结点的中序前驱就是这些结点的左子树的最右下的结点;
	//这些结点的中序后继就是这些结点的右子树的最左下的结点
}

//一边中序遍历一边对遍历到的结点中序线索化
void IN_Thread(BiThread_Tree &T)
{
	if (T != NULL)
	{
		IN_Thread(T->lchild);//左
		visit_BiTnode(T);//根
		IN_Thread(T->rchild);//右
	}

}

void creat_IN_Thread(BiThread_Tree &T)
{
	printf("\n线索二叉树开始中序线索化:。。。");
	if (T != NULL)//树不是空的
	{
		IN_Thread(T);
	}

	if (pre->rchild == NULL)
	{
		pre->rtag=1;//最后一个被中序遍历的结点实现中序线索化
		
	}

	printf("\n线索二叉树中序线索化完成!");
}





//(3)利用中序线索二叉树的线索查找该结点的中序前驱和中序后继
//结点T的中序前驱必定是该节点T的左子树的最右下的结点(结点T的中序前驱结点是 结点T左子树 最后一个被中序遍历到的结点,之后下一个被中序遍历到的就是结点T)
//结点T的中序后继必定是该节点T的右子树的最左下的结点(结点T的中序后继结点是 结点T右子树 第一个被中序遍历到的结点,所以前一个被中序遍历到的就是结点T)

//找中序线索二叉树的结点T的中序前驱(左子树的最后一个被中序遍历到的结点)

//得到在以结点T为根的树中 被最后一个中序遍历(左根右)到的结点(最右下的结点)
BiTnode* LastNode(BiTnode* T)
{
	while (T->rtag == 0)//结点有右孩子
		T=T->rchild;//最后一个被中序遍历到的为结点的右孩子
	
	//结点没有右孩子(T->rtag==1),该子树的最后一个被中序遍历的结点就是根节点T本身
	return T;
}

//得到结点T的中序前驱(结点T的左子树的最后一个被中序遍历访问到的结点)
BiTnode* PreNode(BiTnode* T)
{
	if (T->ltag == 0)//如果结点T有左子树,结点T的中序前驱(结点T的左子树的最后一个被中序遍历访问到的结点)
	{
		return LastNode(T->lchild);
	}
	else if (T->ltag == 1)//如果结点T没有左子树,结点T的中序前驱就是T->lchild (中序线索化后的后继)
		return T->lchild;

}

void visit_IN(BiTnode* p)
{
	printf(" %d ",p->data);
}

//不用递归,利用中序线索二叉树实现二叉树的中序遍历的逆序输出
void put_out_inverse(BiThread_Tree T)
{
	BiTnode* p=NULL;
	for (p= LastNode(T);p!=NULL;p= PreNode(p))
	{
		visit_IN(p);
	}
}


//找到结点T的中序后继结点(结点T的右子树的第一个被中序遍历到的结点(右子树的最左下的结点))

//得到以结点T为根节点的二叉树的的第一个被中序遍历(左根右)到的结点
BiTnode* FistNode(BiTnode* T)
{
	while (T->ltag == 0)//结点有左孩子
	{
		T=T->lchild;
	}
	//结点没有左孩子(T->ltag==1),该二叉树的第一个被中序遍历到的结点就是根节点本身(左根右)
	return T;
}

//得到结点T的中序后继结点(结点T的右子树的第一个被中序遍历到的结点)
BiTnode* PostNode(BiTnode* T)
{
	if (T->rtag == 0)//有右子树:返回结点T的右子树的第一个被中序遍历到的结点
		return FistNode(T->rchild);
	else if (T->rtag == 1)
		return T->rchild;//没右子树
}

//不用递归,利用中序线索二叉树实现二叉树的中序遍历的顺序输出

void put_out(BiThread_Tree T)
{
	BiTnode* p = NULL;
	for (p = FistNode(T); p != NULL; p = PostNode(p))
	{
		visit_IN(p);
	}
}








void test_02()
{
	BiTnode* p = NULL;
	BiTnode* node = NULL;
	BiThread_Tree T=Init_BiThread_root();
	creat_BiThread_Tree(T);
	printf("\n线索二叉树创建成功!(未中序线索化)");
	creat_IN_Thread(T);//完成中序线索化
	printf("\n请输入你想要查找中序前驱的结点的!地址!:");
	scanf_s("%p",&p);
	node = PreNode(p);
	printf("\n %p :%d 的中序前驱:\n %p : %d ",p,p->data,node,node->data);

	printf("\n请输入你想要查找中序后继的结点的!地址!:");
	scanf_s("%p", &p);
	node = PostNode(p);
	printf("\n %p :%d 的中序前驱:\n %p : %d ", p, p->data, node, node->data);

	printf("\n利用中序线索二叉树得到顺序中序遍历:\n");
	put_out(T);
	printf("\n利用中序线索二叉树得到逆序中序遍历:\n");
	put_out_inverse(T);
	
}

int main()
{
	test_02();
	return 0;
}

运行结果:

 手算:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值