重建二叉树

本文介绍如何根据给定的二叉树前序遍历和中序遍历结果重建二叉树,并提供了详细的C语言代码实现。通过递归算法构建二叉树,并演示了前序遍历和后序遍历的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通常树有如下几种遍历方式:

1.前序遍历:先访问根结点,再访问左子结点,最后访问右子结点。

2.中序遍历:先访问左子结点,再访问根结点,最后访问右子结点。

3.后序遍历:先访问左子结点,再访问右子结点,最后访问根结点。

这三种都属于深度优先遍历的方式,与之对应的还有 宽度优先遍历,也叫广度优先遍历。

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},则重建出二叉树如下图所示,并输出它的头结点。二叉树的结构定义如下:

typedef struct BinaryTreeNode
{
	int value;
	struct BinaryTreeNode *pleft;
	struct BinaryTreeNode *pright;
}NODE, *PNODE;

                                                                       

由于在中序遍历中有3个数字是左子树结点的值,因此左子树总共有3个子结点。同样在前序遍历中,根结点之后的3个数字就是左子结点的值,再后面的数字都是右子树结点的值,。这样我们再前序遍历序列和中序遍历序列中分别找到了左右子树对应的子序列。

                       

既然我们已经找到了左右子树的前序遍历序列和中序遍历序列,我们也就可以用同样的方法来构建去分别构建左右子树。也就是说,接下来的事情,我们可以用递归来完成:

PNODE  ConstructCore(int *pre_start, int *pre_end, 
				int *ino_start, int *ino_end)
{
	//前序遍历第一个数字是根结点的值
	int root_val = pre_start[0];
	PNODE root = (PNODE)malloc(sizeof(NODE));
	root->value = root_val;
	root->pleft = root->pright = NULL;

	if (pre_start == pre_end)
	{
		if (ino_start == ino_end && *pre_start == *ino_start)
			return root;
		else
			exit(1);	//输入有误
	}
	
	//在中序遍历中找到根结点的值

	int *ino_root = ino_start;
	while (ino_root <= ino_end && *ino_root != root_val)
		ino_root++;

	if (ino_root == ino_end && *ino_root != root_val)
		exit(1);		//输入有误
	
	int len_left = ino_root - ino_start;
	int *pre_end_left = pre_start + len_left;	//跳过最开始的根结点
	if (len_left > 0)
	{
		//构建左子树
		root->pleft = ConstructCore(pre_start + 1, pre_end_left, 
					ino_start, ino_root - 1);
	}
	if (len_left < pre_end - pre_start)
	{	
		//构建右子树
		root->pright = ConstructCore(pre_end_left + 1, pre_end, 
					ino_root + 1, ino_end);
	}

	return root;
}

PNODE Construct(int *preorder, int *inorder, int len)
{
	if (preorder == NULL || inorder == NULL || len <= 0)
		return NULL;
	
	return ConstructCore(preorder, preorder + len - 1, inorder, inorder + len - 1);
}

为了最后能输出这颗二叉树以检验函数的正确性,我又实现了前序遍历二叉树:

#define OK 1
#define ERROR 0

int print(PNODE pnode)
{
	printf("%d ", pnode->value);
	return OK;
}

int pre_order_traverse(PNODE root, int (*visit)(PNODE pnode))
{
	if (root)
	{
		if (visit(root))
		{
			if (pre_order_traverse(root->pleft, visit))
			{
				if(pre_order_traverse(root->pright, visit))
					return OK;
			}
		}
		return ERROR;
	}
	return OK;
}
当然既然用到了动态分配内存,你就要负责释放掉申请的内存,所以有了下面这段代码(后序遍历):

int free_node(PNODE pnode)
{
	free(pnode);
	pnode = NULL;
	return OK;
}

int post_order_traverse(PNODE root, int (*visit)(PNODE pnode))
{
	if (root)
	{
		if(post_order_traverse(root->pleft, visit))
		{
			if (post_order_traverse(root->pright, visit))
				visit(root);
		}
		else
			return ERROR;
	}
	
	return OK;
}
当然可以不用实现之前的前序遍历,用后序遍历来输出也是可以的。但是用前序遍历来做内存的释放就不行了,因为当你释放了一个结点后,还怎么去找它的左右孩子?有些人可能就要说了,free()之后指针的值并没有发生改变,还可以找的到!可我认为虽然还可以找的到,但这是不好的,所以我在free()之后加了给指针赋值为NULL的语句。

最后是测试用的main函数:

int main()
{
	int pre[] = {1, 2, 4, 7, 3, 5, 6, 8};
	int ino[] = {4, 7, 2, 1, 5, 3, 8, 6};
	
	PNODE tree = Construct(pre, ino, 8);
	pre_order_traverse(tree, print);
	printf("\n");
	
	post_order_traverse(tree, free_node);

	return 0;
}

只是做了个简单的测试,并没有输入太多例子,有兴趣的可以自己实现一下。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fireplusplus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值