题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true。否则返回false。假设输入的数组的任意两个数字都互不相同。
思路:
满二叉树:从高到低,除了叶结点外,所有结点的左右结点都存在。
完全二叉树:比满二叉树少几个叶结点,从左向右放子结点。
平衡二叉树:空树或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树也都是平衡树。、
二叉搜索树:空树或者二叉树的所有结点比它的左子结点大,比它的右子结点小。
例如输入数组{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的左子结点,7是根结点6的右子结点;同样对于序列9, 11, 10的子树而言,10是根结点,9是根结点10的左子结点,11是根结点10的右子结点。
以数组{7, 4, 6, 5}为例,后序遍历的结果中最后一个值5就是根结点。数组的第一个数字7大于5,故此二叉搜索树没有左子树, 7, 4, 6都是右子树结点的值。我们发现4比5小,违背了二叉搜索树的规定。故此数组不是二叉搜索树的后序遍历。
代码实现:
public boolean verifySquenceOfBST(int sequence[]){
if(sequence == null){
return false;
}
return verifySquenceOfBST1(sequence, 0, sequence.length - 1);
}
private boolean verifySquenceOfBST1(int[] sequence, int start, int end) {
if(start >= end){
return true;
}
int root = sequence[end]; //后序遍历的最后一个结点为根结点
//在二叉搜索树中左子树的结点小于根结点
int i = 0;
for(; i < end; ++i){
if(sequence[i] > root){
break;
}
}
//在二叉搜索树中右子树的结点大于根结点
int j = i;
for(; j < end; ++j){
if(sequence[j] < root){
return false;
}
}
//判断左子树是不是二叉树
boolean left = true;
if(i > start){
left = verifySquenceOfBST1(sequence, start, i-1);
}
//判断右子树是不是二叉树
boolean right = true;
if(i < end){
right = verifySquenceOfBST1(sequence, i, end -1);
}
return (left && right);
}
小结:
解决这道题目的关键在于是否能找到二叉搜树的后序遍历的规律。一旦找到规律了,用递归的代码编码相对就简单多了。
扩展:
输入一个数组,判断该数组是不是某二叉搜索树的前序遍历的结果。
思路:
这其实和上面的二叉搜索树的后序遍历规律是一样的,不过后序遍历根结点在后面,前序遍历根结点在前面而已,即第一个就是根结点的值。
代码实现:
public boolean verifySquenceOfBST(int sequence[]){
if(sequence == null){
return false;
}
return verifySquenceOfBST1(sequence, 0, sequence.length - 1);
}
private boolean verifySquenceOfBST1(int[] sequence, int start, int end) {
if(start < end){
return false;
}
int root = sequence[start]; //后序遍历的最后一个结点为根结点
//在二叉搜索树中左子树的结点小于根结点
int i = 1;
for(; i < end; ++i){
if(sequence[i] > root){
break;
}
}
//在二叉搜索树中右子树的结点大于根结点
int j = i;
for(; j < end; ++j){
if(sequence[j] < root){
return false;
}
}
//判断左子树是不是二叉树
boolean left = true;
if(i > start+1){
left = verifySquenceOfBST1(sequence, start+1, i-1);
}
//判断右子树是不是二叉树
boolean right = true;
if(i < end){
right = verifySquenceOfBST1(sequence, i, end);
}
return (left && right);
}
小思:
如果面试题是要求处理一颗二叉树的遍历序列,我们可以先找到二叉树的根结点,再基于根结点把整颗树的遍历序列拆分成左子树对应的子序列和右子树对应的子序列,接下来再递归地处理这两个子序列。