一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
题目:给定一个数组, 判断该数组是否为某个二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。本题有一个条件是输入数组元素互不相同。
解题思路
本题的考察点是希望用二叉树的特性来解决本题,首先二叉树具有一个特性是左子树的所有值都小于根节点,右子树的所有值都大于根节点。
根据这个特性,如果一个序列确实是一颗二叉树的后序遍历结果,那么我们可以确定的是根节点必然是序列的最后一个节点,此时我们需要确定的是这棵树的左子树和右子树,因为右子树的所有值都大于根节点,因此我们可以从序列的首元素位置开始寻找,找到第一个大于等于根节点的元素,则此元素即为右子树的起点。之后继续向后判断是否之后元素都大于根节点即可,如果都满足则递归判断左右子树,可得代码如下:
public static boolean verifyPostorder(int[] postorder) {
int len = postorder.length;
if(len<2) return true;
return verify(postorder, 0, len-1);
}
public static boolean verify(int[] postorder, int cur, int max){
if(cur>=max) return true;
// 首先得到根节点
int root = postorder[max];
// 之后得到左右子树的区域
int right = cur;
for(int i=cur;i<=max;i++){
if(postorder[i]>=root) {
right = i;
break;
}
}
// while (right < max && postorder[right] < root){ // 从当前区域找到第一个大于根节点的,说明后续区域数值都在右子树中
// right++;
// }
// 此时左子树的范围(cur---right-1)
// 右子树的范围(right---root-1)
// 查找左子树是否都小于根(根据上方此步可跳过),右子树是否都大于根
for(int i=right;i<max;i++){
if(postorder[i]<root) return false;
}
// 递归检查左右子树
return verify(postorder, cur, right-1)&&verify(postorder, right, max-1);
}
本题还有一个有意思的解法是单调栈。首先明白序列的最后一个节点的值必然是根节点,那么往前如果比根节点大则必然是右子树,如果比此值小则必然是左子树,具体代码如下,可以自己模拟体会一下:
public boolean verifyPostorder(int[] postorder) {
int len = postorder.length;
if(len<2) return true;
int postValue = Integer.MAX_VALUE;
ArrayDeque<Integer> stack = new ArrayDeque<>();
for(int i=len-1;i>=0;i--){
if(postorder[i]>postValue) return false;
while(!stack.isEmpty()&&postorder[i]<stack.peek()){
postValue = stack.pop();
}
stack.push(postorder[i]);
}
return true;
}