输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/
2 6
/
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
提示:
- 数组长度 <= 1000
分析:
二叉搜索树:根节点的值一定大于它的左子树每个节点的值,小于它的右子每个右节点的值。
后序遍历:按照 左子树 → 右子树 → 根 的顺序遍历
方法1:分治算法
根据后序遍历的定义可以知道数组最后一个元素一定为该二叉搜索树的根,且根前面是左子树+右子树的形式,由二叉搜索树的定义可以知道,左子树都小于根,右子树都大于根,因此我们可以根据根节点的值来找出左子树和右子树,如果没有找到,那么该树就不是二叉搜索树,如果找到,我们继续判断两个子树是否为二叉搜索树,直到子树只剩两个节点(任意两个节点都能组成二叉搜索树)。
时间复杂度:O(n^2)
空间复杂度:O(n)
class Solution {
public boolean verifyPostorder(int[] postorder) {
return verifyPostorder(postorder, 0, postorder.length-1);
}
public boolean verifyPostorder(int[] postorder, int start, int end) {
//长度小于2直接返回true
if (end - start < 2) {
return true;
}
//定义根节点
int root = postorder[end];
//定义双指针分别指向头尾
int i = start, j = end - 1;
//遍历,根节点左子树一定比它小,右子树一定比它大
//找到右子树
while (i < j && postorder[j] > root) {
j--;
}
//找到左子树
while (i < j && postorder[i] < root) {
i++;
}
//i == j 表示根节点在两子树中间,继续遍历两子树
return i == j && verifyPostorder(postorder, start, i) && verifyPostorder(postorder, i + 1, end - 1);
}
}
方法2:单调栈
我们将后序遍历的结果反过来可以得到一个 根 → 右子树 → 左子树 的结构,当我们遍历这个结构的时候,当一个节点大于当前节点时,那么这个节点一定为当前节点的右节点,如果小于这个当前节点,那么这个节点一定为前面某个节点的左节点。那么我们可以定义一个单调栈(栈满足单调递增或递减)来存储根节点,然后我们假设当前根节点为左节点,然后定义一个当前根节点的根节点(默认整形最大),那么当前根节点的子树一定满足每个节点的值 小于 当前节点的根节点的值,不满足则不为二叉搜索树,接着我们按循序遍历,当为右节点时,入栈,当为左节点,出栈直到找到该左节点的根节点,将原来根节点的值赋为当前根节点,入栈左节点,依次循环,直到有不满足条件或遍历完所有节点为止。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public boolean verifyPostorder(int[] postorder) {
//定义单调栈
Deque<Integer> stack = new ArrayDeque<>();
//定义指针
int i = postorder.length-1;
//定义当前节点的根节点,默认为为最大整形
int root = Integer.MAX_VALUE;
//遍历
while(i >= 0){
//如果当前节点大于根节点,则一定不为搜索树
if(postorder[i] > root){
return false;
}
//为左节点,出栈直到为空或者找到当前节点的根节点
while(!stack.isEmpty() && postorder[i] < stack.peekLast()){
root = stack.pollLast();
}
//入栈当前节点
stack.offerLast(postorder[i]);
i--;
}
return true;
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof