线索二叉树(前序,中序,后序线索化以及遍历)

其实在这之前我想了很久,这个线索化二叉树我个人感觉是比实现二叉链表要难,很抽象的一个东西。好了,话先不多说,老规矩,先上代码:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int type;
typedef struct leadtree
{
	type val;
	int ltag;
	int rtag;
	struct leadtree* left;
	struct leadtree* right;
}leadtree;
void creat(leadtree** root);
void leadfront(leadtree** root);
void leadmid(leadtree** root);
void leadback(leadtree** root);
void traversefront(leadtree** root);
void traversemid(leadtree** root);
void traverseback(leadtree** root);
#define _CRT_SECURE_NO_WARNINGS 1
#include "leadh.h"
leadtree* pre = NULL;
void creat(leadtree** root)
{
	//这里强调一下,这个断言指的传进来的指向根节点的指针,如果都没有指向根节点的指针,就报错
	assert(root);
	printf("是否要继续生成左孩子还是右孩子->0,1\n");
	int ret = 0;
	scanf("%d", &ret);
	if (0 == ret)
	{
		*root = NULL;
		return;
	}
	else
	{
		leadtree* tem = (leadtree*)calloc(1, sizeof(leadtree));
		assert(tem);
		*root = tem;
		printf("请输入你要保存的值->\n");
		scanf("%d", &(*root)->val);
		creat(&(*root)->left);
		creat(&(*root)->right);
	}
}
static void visit(leadtree** root)
{
	if ((*root)->left == NULL)
	{
		(*root)->left = pre;
		(*root)->ltag = 1;
	}
	if (pre != NULL && pre->right == NULL)
	{
		pre->right = *root;
		pre->rtag = 1;
	}
	pre = *root;
}
static void front(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		visit(root);
		if ((*root)->ltag == 0)
			front(&(*root)->left);
		if ((*root)->rtag == 0)
			front(&(*root)->right);
	}
}
static void mid(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		mid(&(*root)->left);
		visit(root);
		mid(&(*root)->right);
	}
}
static void back(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		back(&(*root)->left);
		back(&(*root)->right);
		visit(root);
	}
}
//前序线索化二叉树
void leadfront(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
	{
		front(root);
		pre->rtag = 1;
	}
}
//中序线索化二叉树
void leadmid(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
	{
		mid(root);
		pre->rtag = 1;
	}
}
//后序线索化二叉树
void leadback(leadtree** root)
{
	assert(root);
	if (*root == NULL)
		return;
	else
		back(root);
}
//前序线索化找后继
static leadtree* next(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->rtag == 1)
		return (*root)->right;
	else
	{
		if ((*root)->ltag != 1)
		{
			*root = (*root)->left;
			return *root;
		}
		else
		{
			*root = (*root)->right;
			return *root;
		}
	}
}
//遍历前序线索化
void traversefront(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = *root; tem != NULL; tem = next(&tem))
			printf("%d->", tem->val);
	}
}
//中序线索化找后继
static leadtree* nextmid(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->rtag == 1)
		return (*root)->right;
	else
	{
		if ((*root)->rtag != 1)
		{
			*root = (*root)->right;
			while ((*root)->ltag == 0)
				*root = (*root)->left;
			return *root;
		}
	}
}
static leadtree* first(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else
	{
		if ((*root)->ltag != 1)
		{
			while ((*root)->ltag == 0)
				*root = (*root)->left;
			return *root;
		}
		else
			return *root;
	}
}
//遍历中序线索化二叉树
void traversemid(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = first(root); tem != NULL; tem = nextmid(&tem))
			printf("%d->", tem->val);
	}
}
//后序线索化二叉树找前驱
static leadtree* prev(leadtree** root)
{
	if (*root == NULL)
		return NULL;
	else if ((*root)->ltag == 1)
		return (*root)->left;
	else
	{
		if ((*root)->rtag != 1)
		{
			*root = (*root)->right;
			while ((*root)->rtag == 0)
				*root = (*root)->right;
			return *root;
		}
		else
		{
			*root = (*root)->left;
			while ((*root)->rtag == 0)
				*root = (*root)->right;
			return *root;
		}
	}
}
//遍历后序线索二叉树
void traverseback(leadtree** root)
{
	if (*root == NULL)
		return;
	else
	{
		for (leadtree* tem = *root; tem != NULL; tem = prev(&tem))
			printf("%d->", tem->val);
	}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include "leadh.h"
void test1()
{
	leadtree* root;
	creat(&root);
	leadfront(&root);
	//leadmid(&root);
	//leadback(&root);
	traversefront(&root);
	//traversemid(&root);
	//traverseback(&root);
}
int main()
{
	test1();
	return 0;
}

还是老样子,三个文件,就不一一说了。

我们可以看到的是,线索化二叉树的一个节点比二叉树的一个节点要多两个域,这两个域其实就是标记,线索化二叉树其实说起来很简单,就是利用它的前中后序遍历来边线索化,边边历,听起来也很简单,感觉没什么,但是这个在我们用代码实现的时候就不是这么简单了,因为你一不小心就很造成死循环。

先来看看下面的图:

这个就是手画的线索化完之后的二叉树,手画很简单,就是指针指向他的遍历序列的前驱,后指针指向遍历序列的后继。

现在我们说一下代码实现的思路以及注意事项:

首先,就是头文件,我相信没有什么要说的了。大家应该都是懂的。

其次,就是写函数的那个文件了,我先写的是前序线索化二叉树,但是在这里我先给大家说一下中序线索化二叉树,因为前序线索化二叉树有坑,先说中序线索化二叉树。其实也很简单,就是先遍历左子树,在线索化,线索化完当前的节点之后,在遍历右子树,就是这样。而具体的线索化过程在visit这个函数里面,具体不说了,(左指针一般都是指向前驱,右指针一般都是指向后继) 但是大家仔细的想一下,当我们用前序线索化二叉树的时候就会出现问题。什么问题呢?就是要在遍历他的左右子树之前,要判断一下,是否指向的是自己的子树。如果是,则遍历,如果不是,就不遍历,要不然会造成死循环(下面会详细讲)。

其次就是前序和中序线索化完之后,因为pre这个指针一定在树的最右面的那个节点,所以此时pre->right一定会是NULL,这个是因为我们在创建二叉树的时候,停止生成左右孩子的时候,我们就把这个节点赋值成了NULL,但是在线索化二叉树中,空指针就应该是指向的线索,所以此时我们不要多此一举,直接在他线索化完之后,直接就把他的右标记域改成1就好。

然后很多小伙伴可能有些疑惑,就是为什么这次创建二叉树的时候,为什么开辟空间不用malloc,而用了calloc,这个是因为有了两个标记域,所以一定要把标记与给初始化为0,可能又有些人说,就算不初始化,就算他是“垃圾值”,我们最后线索化的时候就把他改成1了,不影响,但是你仔细想一下,他只能改的是空指针域,那些没有空的指针域怎么办?是不是还是“垃圾值”,这样在遍历线索二叉树的时候怎么办?怎么找他的后继节点?所以,我们就用了calloc来开辟空间,开辟好空间直接就把左右标记赋值为0。

其次就是前序线索化的时候,很多人会疑惑,为什么形成死循环,但是我们用再用他遍历普通的二叉树的时候,为什么就不会形成死循环,来看看下面这幅图:

根据上面的图,我们可以看到,4的直接前驱是2,但是如果在线索化的时候,不根据他的左右标记来判断,就很在4的这里形成死循环,因为此时4指向了2,线索化完4,访问他的左指针域,就直接到了2,所以这样形成了死循环。所以一定要注意这个。中序和后序就没有此问题。

再就是在寻找前驱和后继的时候,前序线索化不好找前驱,后线索化不好找后继,此时就需要重新比那里二叉树来找他的前驱和后继。

以上就是这篇文章的内容,如果对你有用的话,就点一下赞吧!!!!谢谢支持!! 

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值