二叉搜索树的后序遍历(递归做法+类似方法题目)

题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果,如果是则返回true,否则返回false。假设输入的数组的任意两个数字都不相同。例如输入数组{5,7,6,9,11,10,8},则返回true,因为它能构建出如下图所示的二叉搜索树,而输入数组{7,4,6,5},返回false,因为没有哪棵二叉搜索树的后序遍历为它。

在这里插入图片描述
分析:

在后序遍历得到的序列中,最后一个数字是树的根节点的值。数组中前面的数字可以分为两部分:第一部分是左子树节点的值,它们都比根节点的值小;第二部分是右子树节点的值,它们都比根节点的值大。

以数组{5,7,6,9,11,10,8};为例,后序遍历结果的最后一个数字8就是根节点的值。在这个数组中,前3个数字5、7和6都比8小,是值为8的节点的左子树节点;后3个数字9、11和10都比8大,是值为8的节点的右子树节点。

我们接下来用同样的方法确定与数组每一部分对应的子树的结构。这其实就是一个递归的过程。对于序列5,7,6,最后一个数字6是左子树的根节点的值。数字5比6小,是值为6的节点的左子节点,而7则是它的右子节点。同样,在序列{9,11,10};中,最后一个数字10是右子树的根节点,数字9比10小,是值为10的节点的左子节点,而11则是它的右子节点。

我们再来分析另一个整数数组{7,4,6,5}。后序遍历的最后一个数字是根节点,因此根节点的值是5。由于第一个数字7大于5,因此在对应的二叉搜索树中,根节点上是没有左子树的,数字7、4和6都是右子树节点的值。但我们发现在右子树中有一个节点的值是4,比根节点的值5小,这违背了二叉搜索树的定义。因此,不存在一棵二叉搜索树,它的后序遍历结果是{7,4,6,5}。

代码:

bool verifyPostorder(int* postorder, int postorderSize){
    
    
	if(postorder == NULL ||postorderSize <= 0)
		return false;

	int root = postorder[postorderSize-1];
    //二叉搜索树中左子树节点值小于根节点值
	int i = 0;
	for(i = 0;i < postorderSize - 1;i++){
    
    
		if(postorder[i] > root)
			break;
	}
	//二叉搜索树中右子树节点值大于根节点值
	int j = i;
	for(j = i;j < postorderSize - 1;j++){
    
    
		if(postorder[j] < root)
			return false;
	}
   //判断左子树是不是二叉搜索树
	bool left = true;
	if(i > 0)
		left = verifyPostorder(postorder,i);
  //判断右子树是不是二叉搜索树
	bool right = true;
	if(i < postorderSize-1)
		right = verifyPostorder(postorder + i,postorderSize - i- 1);

	return (left && right);
}

类似方法题目:

1、输入一个整数数组,判断该数组是不是某二叉搜索树的前序遍历结果。这和前面问题的后序遍历很类似,只是在前序遍历得到的序列中,第一个数字是根节点的值。大体思路是不变的,只是一些范围需要调整

2、题目:输入某二叉树前序遍历和中序遍历的结果,请重建该二叉树,假设输入的前序遍历和中序遍历中都不含有重复数字。例如,两个序列为:

前序遍历(根左右)
1 2 4 7 3 5 6 8
中序遍历(左根右)
4 7 2 1 5 3 8 6
则重建的二叉树如下,并返回头结点。

在这里插入图片描述

分析:
前序遍历的中的第一个节点总是二叉树的根节点,而在中序遍历中二叉树的根节点在中间 。左子树的节点值在根节点的左边,右子树的节点值在根节点的右边。因此我们扫描中序序列,找到根节点的值,就能得到左右子树的对应中序的子序列,进而得到左右子树对应的前序的子序列。

如上面的两个序列中,由序列1可知 1为根节点,在序列2中扫描找到根节点1,根节点前面3个节点为左子树节点中序序列。后4个节点为右子树节点中序序列 ,则在序列1中根节点1后面3个节点为左子树前序序列,再后面4个节点为右子树前序序列。

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

struct TreeNode * ConstructCore(int *StartPreOrder,int *EndPreOrder,int *StartInOrder,int *EndInOrder){
    
    
	int rootvalue = StartPreOrder[0];

	struct TreeNode *root=(struct TreeNode*)malloc(sizeof(struct TreeNode));

	root->val   = rootvalue;
	root->left  = NULL;
	root->right = NULL;

	int *rootinorder = StartInOrder;
	
	while(rootinorder <= EndInOrder && *rootinorder != rootvalue)
		rootinorder++; // 在中序遍历中找到根节点的位置
		
	int leftlength = rootinorder - StartInOrder; //左子树节点的数目

	int *leftPreOrderEnd = StartPreOrder + leftlength;//前序序列中左子树结束的位置

	if(leftlength>0){
    
    
		root -> left = ConstructCore(StartPreOrder+1,leftPreOrderEnd,StartInOrder,rootinorder-1);
	}
	if(leftlength < EndPreOrder - StartPreOrder){
    
    
		root -> right = ConstructCore(leftPreOrderEnd+1,EndPreOrder,rootinorder+1,EndInOrder);
	}
	return root;
}
struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize) {
    
    
	if(preorderSize == 0)
		return NULL;
	return ConstructCore(preorder,preorder+preorderSize-1,inorder,inorder+inorderSize-1);

}

如果题中要求处理一棵二叉树的遍历序列,则可以先找到二叉树的根节点,再基于根节点把整棵树的遍历序列拆分成左子树对应的子序列和右子树对应的子序列,接下来再递归地处理这两个子序列。

Guess you like

Origin blog.csdn.net/scarificed/article/details/120512281