二叉排序树(Binary Sort Tree),又称二叉查找树。它是一颗空树,或者具有下列性质:
若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树分别为二叉排序树。
二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5 / \ 2 6 / \ 1 3
示例 1:
输入: [1,6,3,2,5] 输出: false
示例 2:
输入: [1,3,2,6,5] 输出: true
递归分治
//因为是后序遍历:左右根, 所以最后的一定是根节点 ,二叉排序树的左子树都小于根节点,右子树反之。利用好这两个特征就可以证明
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length - 1);
}
boolean recur(int[] postorder,int i, int j){ //在i j这个区间 (也就是说确定i-j这个坐标的树是否符合二叉搜索树)
if(i >= j){ //前面的坐标大于等于后面的坐标, 说明树的结点只剩一个或者不剩了.
return true;
}
int p = i; //p是负责遍历的坐标 (从i开始)
while(postorder[p] < postorder[j]) p++;
int m = p; //如果发现大于尾结点的数(则这个数及之后都是右子树)
while(postorder[p] > postorder[j]) p++; //(右子树的所有子节点都应该大于根节点
//如果遍历结束之后,发现有小于根节点的节点存在,则不符合条件
//接下来一直遍历,直到最后一层叶子节点
return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);//(第二次分层的时候记得把根节点滤除, 所以到 j - 1)
辅助单调栈
//就是找子树的分叉点,保证root一直是正在经历分叉的树的最大点,在下一次分叉之前,保证所有元素都小于这个最大点。
//找的第一个分叉点(一直向右下探索(数组为递增) 找到最大点,然后就是最大点的同层左子树,因为最小子树(也就是只有两个点),是最右端的,所以她的同层左子树也是出了该点的最大的值(所以也就把root换成左子树了),然后一直向上遍历相对最右子树的左节点,直到遍历完了整个大二叉树的右子树,在遍历这样大二叉树左子树,如果该数组符合这些,则成立。)
//遍历 postorderpostorder 所有节点,各节点均入栈 / 出栈一次,使用 O(N) 时间。
class Solution {
public boolean verifyPostorder(int[] postorder) {
Stack<Integer> stack = new Stack<>();
int root = Integer.MAX_VALUE;
for(int i = postorder.length - 1; i >= 0; i--){ //倒叙分别是根 右 左 (如果没遇到递减说明一直在增,也就是一直在右子树.)
//一直把右子树遍历到底,所以当前的结点一直都是同一层的左子树或者下一层的右子树()
if(postorder[i] > root) return false; //右 > 根 > 左 这个序列会全部入栈()所以 root应当是当前的根或者右子树
//他会一直向右下方向,直到遍历到最右下的那个小二叉树时发现递减。
while(!stack.isEmpty() && stack.peek() > postorder[i]){ 界
root = stack.pop(); //往回出栈,因为此时是未检测过的最右的子树的左节点,所以当找到小于此节点应该就是父节点的根节点,所以此时的root为这个分叉的左节点的父节点
}
//将这个左节点入栈(此时root最大,这个节点第二大。)
stack.add(postorder[i]); //无论怎样都倒序入栈
}
return true;
}
}