剑指 offer 33:二叉搜索树的后序遍历序列
二叉搜索树
二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中的每个节点:
- 如果其左子树存在,则左子树中每个节点的值都不大于该节点的值;
- 如果其右子树存在,则右子树中每个节点的值都不小于该节点的值。
示例
递归
数组最后一个元素一定是根节点,根节点可以把前面的元素分成两组,从某一个元素之前的所有元素都不大于根节点的值,某一个元素到根节点位置都不小于根节点的值,否则就不是二叉搜索树的后序遍历序列,递归判断两个子部分,即不大于根节点的前半部分和不小于根节点的后半部分。
class Solution {
public boolean verifyPostorder(int[] postorder) {
//二叉树节点个数小于等于 1
if(postorder.length<=1){
return true;
}
return verify(postorder,0,postorder.length-1);
}
private boolean verify(int[] postorder,int left,int right){
//只有一个节点,返回 true
if(left>=right){
return true;
}
int mid=right;
for(int i=left;i<right;i++){
if(postorder[i]>=postorder[right]){
mid = i;
break;
}
}
//判断右子树的节点是否都大于等于根节点
for(int i= mid+1;i<right;i++){
if(postorder[i]<postorder[right]){
return false;
}
}
return verify(postorder,left,mid-1) && verify(postorder,mid,right-1);
}
}
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
空间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),递归栈占用的空间。
栈
后序遍历的倒序(根右左),栈顶元素大于遍历值直接将元素添加到栈中,保证了右子树的正确,此时判断左子树是不是都小于 root 就可以了。
如果栈为空就把当前元素压栈。如果栈不为空,并且当前元素大于栈顶元素,说明是升序(右子树),那么说明当前元素是栈顶元素的右子节点,把当前元素压栈,如果一直升序,就一直压栈。当前元素小于栈顶元素,说明当前元素是某个节点的左子节点,我们要找到这个左子节点的父节点,就让栈顶元素出栈,直到栈为空或者栈顶元素小于当前值为止,其中最后一个出栈的就是当前元素的父节点(再进入循环时,就是当前父节点的左子树进栈,要判断左子树的节点是否小于根节点,如果不是返回 false)。
这种方法比较绕!
class Solution {
public boolean verifyPostorder(int[] postorder) {
Stack<Integer> s = new Stack<>();
int root = Integer.MAX_VALUE;
for(int i=postorder.length-1;i>=0;i--){
if(postorder[i]>root){
return false;
}
while(!s.empty() && s.peek()>postorder[i]){
root = s.pop();
}
s.push(postorder[i]);
}
return true;
}
}
时间复杂度是
O
(
n
)
O(n)
O(n),postorder 中的所有节点进栈出栈一次,使用
O
(
n
)
O(n)
O(n)的时间。
空间复杂度
O
(
n
)
O(n)
O(n),最差情况下,单调栈存储所有节点,使用
O
(
n
)
O(n)
O(n)额外空间。