根据前序和中序,构建二叉树

在学习数据结构的时候,经常会考到用前中后序三者之二来重构二叉树或者得到另外一个未知的遍历顺序

往往对着图和数字我们可以轻松地把树画出来,但是如果要我们用代码实现它,又要怎么做呢?

其实过程是一样的,只要牢记前中后序的产生过程,逆向推导,加以严谨的程序控制就可以完成了。


  • 前序遍历:也叫先根遍历,即先访问树的根节点,然后再依次递归访问左子树和右子树
  • 中序遍历:也叫中根遍历,即先访问左子树,然后访问根节点,最后再访问右子树

假设我们的树长这个样子



那么就可以得到:

前序遍历:1 2 4 7 5 3 6 8
中序遍历:4 7 2 5 1 3 8 6

下一步用代码重构

定义如下数据结构:树的节点
struct BTreeNode
{
	int m_ivalue;
	BTreeNode* m_pleft;
	BTreeNode* m_pright;
};

观察前序和中序的产生过程,不难发现:
  • 前序的第一个元素就是根节点
  • 而相应的根节点则在中序中间(不一定是正中间),中序中,根节点位置的左边都属于左子树,而右边则属于右子树
  • 并且可以发现,前序中,是左子树先“扎堆”,然后是右子树”扎堆“,也即不会发生交叉现象

以我们的数据为例,从前序可以知道 1 就是根节点,从中序可以得知,4,7,2,5 都是在左子树中,1,3,8,6 则都在右子树中。


看看现在我们得到的有什么:
  • 一个简单通过前序就能拿到的根节点
  • 通过遍历可以从中序中划分出来的左子树节点群和右子树节点群
  • 通过以上两步,可以确定左右子树各自的前中序,比如左子树,他的前序是 2,4,7,5 ,而其中序是 4,7,2,5
于是可以想到,可以用 递归 来重构树了!
从根节点出发,分别递归设置左右子树,当跳出递归时,整颗树都重构完毕

代码实现如下:
BTreeNode* build(int* preOrder, int* inOrder, int length)
{
	if (preOrder == NULL || inOrder == NULL || length == 0)		//前中序为空
		return NULL;
	else
		return buildcore(preOrder, preOrder + length - 1, inOrder, inOrder + length - 1);
}

BTreeNode* buildcore(int* preOrderStart, int* preOrderEnd, int* inOrderStart, int* inOrderEnd)
{
	BTreeNode* root = new BTreeNode();		//new 一个根节点,在递归过程中也可以称为子树根节点
	root->m_ivalue = *preOrderStart;
	root->m_pleft = root->m_pright = NULL;	//初始化为NULL

	if (preOrderStart == preOrderEnd){		
		if (inOrderEnd == inOrderStart && *preOrderStart == *inOrderStart)
			return root;
		else
			throw std::exception("Invalid input");
	}

	int* rootInOrder = inOrderStart;

	//在中序中遍历查找根所在的位置
	while (rootInOrder != inOrderEnd && *rootInOrder != root->m_ivalue)	
		++rootInOrder;
	if (rootInOrder == inOrderEnd && *rootInOrder != root->m_ivalue)
		throw std::exception("Invalid input");

	int leftLength = rootInOrder - inOrderStart;
	int* leftPreOrderEnd = preOrderStart + leftLength;
	//构建左子树
	if (leftLength > 0){
		root->m_pleft = buildcore(preOrderStart + 1, leftPreOrderEnd, inOrderStart, rootInOrder - 1);
	}

	//构建右子树
	if (leftLength < inOrderEnd - inOrderStart){
		root->m_pright = buildcore(leftPreOrderEnd + 1, preOrderEnd, rootInOrder + 1, inOrderEnd);
	}

	return root;

}

小结:要深刻理解前中后序的产生过程及其特点,这是构建树的关键。要理解用递归来处理树的问题,大而化小,分而治之

根据前序遍历和中序遍历结果构建二叉树是一种常见的算法问题。前序遍历的顺序是根节点 -> 左子 -> 右子中序遍历的顺序是左子 -> 根节点 -> 右子。通过这两个遍历结果,我们可以构建出原始的二叉树结构。 构建过程大致可以分为以下步骤: 1. 前序遍历的第一个元素总是的根节点。 2. 在中序遍历结果中找到根节点的位置,这将中序遍历结果分为两部分,左边是所有左子的节点,右边是所有右子的节点。 3. 根据中序遍历中左子和右子的节点数量,可以在前序遍历结果中分割出左子和右子前序遍历序列。 4. 递归地使用上述方法分别构建左子和右子。 具体实现时,可以用一个辅助函数来进行递归操作,函数接收前序遍历序列和中序遍历序列的子序列,返回构建好的子的根节点。 这里提供一个简化的伪代码示例: ``` function buildTree(preorder, inorder) { if (preorder.length == 0 || inorder.length == 0) { return null; } // 前序遍历的第一个值是根节点 let rootVal = preorder[0]; let root = new TreeNode(rootVal); // 在中序遍历中找到根节点的位置 let midIndex = inorder.indexOf(rootVal); // 构建左子 root.left = buildTree(preorder.slice(1, midIndex + 1), inorder.slice(0, midIndex)); // 构建右子 root.right = buildTree(preorder.slice(midIndex + 1), inorder.slice(midIndex + 1)); return root; } ``` 注意:这个伪代码仅为了说明构建过程,实际实现时可能需要处理一些边界情况,并且在实际编程语言中可能需要根据具体语言特性进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值