时间: 2019-06-26
题目链接:Leetcode
tag:分治算法 递归
难易程度:中等
题目描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true
,否则返回 false
。假设输入的数组的任意两个数字都互不相同。
参考如下:
5
/ 2 6
/ 1 3
示例1:
输入: [1,6,3,2,5]
输出: false
示例2:
输入: [1,3,2,6,5]
输出: true
提示
1.数组长度<=1000
本题难点
具体思路
根据二叉搜索树的定义,可以通过递归,判断所有子树的 正确性 (即其后序遍历是否满足二叉搜索树的定义) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。
终止条件:当 i≥j ,说明此子树节点数量 ≤1 ,无需判别正确性,因此直接返回 true ;
递推工作:
划分左右子树:遍历后序遍历的 [i,j] 区间元素,寻找 第一个大于根节点 的节点,索引记为
m。此时,可划分出左子树区间[i,m?1] 、右子树区间[m,j?1] 、根节点索引 j
判断是否为二叉搜索树
左子树区间[i,m?1] 内的所有节点都应 <postorder[j] 。而第 1.划分左右子树 步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
右子树区间[m,j?1] 内的所有节点都应postorder>[j] 。实现方式为遍历,当遇到 ≤postorder[j] 的节点则跳出;则可通过p=j 判断是否为二叉搜索树。
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder,0,postorder.length-1);
}
public boolean recur(int[] postorder,int left,int right){
//当 i≥j ,说明此子树节点数量 ≤1 ,无需判别正确性,因此直接返回 true
if(left > right){
return true;
}
//划分左右子树
int p = left;
//划分出左子树区间[i,m?1]
while(postorder[p] < postorder[right]){
p++;
}
//寻找第一个大于根节点的节点,索引记为 m
int m = p;
//右子树区间[m,j?1]
while(postorder[p] > postorder[right]){
p++;
}
//左子树区间[i,m?1] 内的所有节点都应 <postorder[j]
//右子树区间[m,j?1] 内的所有节点都应postorder[j]
return p == right && recur(postorder,left,m-1) && recur(postorder,m,right-1);
}
}
复杂度分析:
解题思路
辅助单调栈,后序遍历倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。
为什么要用单调栈呢,因为往右子树遍历的过程,value是越来越大的,一旦出现了value小于栈顶元素value的时候,就表示要开始进入左子树了。
单调栈帮我们记录了这些节点,只要栈顶元素还比当前节点大,就表示还是右子树,要移除,因为我们要找到这个左孩子节点直接连接的父节点,也就是找到这个子树的根,只要栈顶元素还大于当前节点,就要一直弹出,直到栈顶元素小于节点,或者栈为空。栈顶的上一个元素就是子树节点的根。
接下来,数组继续往前遍历,之后的左子树的每个节点,都要比子树的根要小,才能满足二叉搜索树,否则就不是二叉搜索树。
代码
class Solution {
public boolean verifyPostorder(int[] postorder) {
// 单调栈使用,单调递增的单调栈
Deque<Integer> stack = new LinkedList<>();
// 表示上一个根节点的元素,这里可以把postorder的最后一个元素root看成无穷大节点的左孩子
int pervElem = Integer.MAX_VALUE;
// 逆向遍历,就是翻转的先序遍历
for (int i = postorder.length - 1;i>=0;i--){
// 左子树元素必须要小于递增栈被peek访问的元素,否则就不是二叉搜索树
if (postorder[i] > pervElem){
return false;
}
while (!stack.isEmpty() && postorder[i] < stack.peek()){
// 数组元素小于单调栈的元素了,表示往左子树走了,记录下上个根节点
// 找到这个左子树对应的根节点,之前右子树全部弹出,不再记录,因为不可能在往根节点的右子树走了
pervElem = stack.pop();
}
// 这个新元素入栈
stack.push(postorder[i]);
}
return true;
}
}
每日一题 - 剑指 Offer 33. 二叉搜索树的后序遍历序列
原文:https://www.cnblogs.com/ID-Wangqiang/p/13218616.html