33. 二叉搜索树的后序遍历序列
思路:分治
由于二叉搜索树具有左子树小于根节点,右子树大于根节点的性质,通过递归判断是否为二叉搜索树。
算法流程:
-
终止条件:当 i ≥ j i\ge j i≥j,说明此子树节点数量 ≤ 1 \leq 1 ≤1,无需判别正确性,直接返回true
-
递归工作:
- 划分左右子树:遍历后序遍历的[i,j]区间元素,寻找第一个大于根节点的节点,索引为m,此时可以划分左子树区间为[i,m-1],右子树区间为[m,j-1],根节点索引为j
- 分别判断左子树和右子树是否为二叉搜索树
- 返回值:所有节点遍历完并且所有子树都正确才可判定正确
class Solution { public: bool verifyPostorder(vector<int>& postorder) { return recur(postorder,0,postorder.size()-1); } bool recur(vector<int>& postorder,int left,int right){ if(left>=right) return true; int p=left; while(postorder[p]<postorder[right]) ++p; int m=p; while(postorder[m]>postorder[right]) ++m; return m==right&&recur(postorder,left,p-1)&&recur(postorder,p,right-1); } };
时间复杂度 O( n 2 n^2 n2)
空间复杂度 O(n)
思路二:辅助单调栈
后序遍历的逆序:根节点|右子树|左子树。类似先序遍历的镜像。
设后序遍历倒序的列表为[ r n , r n − 1 ⋯ r 1 r_n,r_{n-1}\cdots r_1 rn,rn−1⋯r1],遍历此列表,设索引为i,若为二叉搜索树,则有:
- 当节点值 r i > r i + 1 r_i>r_{i+1} ri>ri+1,节点 r i r_i ri在节点 r i + 1 r_{i+1} ri+1的右子节点
- 当节点值 r i < r i + 1 r_i<r_{i+1} ri<ri+1,节点 r i r_i ri一定是某root的左子节点,且root为最接近 r i r_i ri的节点
- 当遍历到递减节点 r i < r i + 1 r_i<r_{i+1} ri<ri+1,若为二叉搜索树,则对于后序遍历中节点 r i r_i ri右边的任意节点 r x ∈ [ r i − 1 , r i − 2 , ⋯ , r 1 ] r_x\in[r_{i-1},r_{i-2},\cdots,r_1] rx∈[ri−1,ri−2,⋯,r1],必有节点值 r x < r o o t r_x<root rx<root
考虑使用单调栈实现遍历递减的最近的节点:
- stack存储值递增的节点
- 每当遇到值递减的节点 r i r_i ri,则通过出栈来更新节点 r i r_i ri的父节点root
- 每次判断
r
i
r_i
ri和root的值关系:
- 若 r i r_i ri>root说明不满足二叉搜索树的定义,返回false
- 若 r i r_i ri<root说明满足二叉搜索树定义,继续遍历
过程:
- 初始化:单调栈,父节点root= + ∞ +\infty +∞
- 倒序遍历:记每个节点为
r
i
r_i
ri:
- 判断:若 r i > r o o t r_i>root ri>root,说明不满足定义,返回false
- 更新父节点root:当栈不为空且 r i < s t a c k . t o p ( ) r_i<stack.top() ri<stack.top()时,执行循环出栈
- 入栈
- 若遍历完成,说明满足定义,返回true
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
stack<int> stk;
int root=INT_MAX;
for(int i=postorder.size()-1;i>=0;--i){
if(postorder[i]>root)return false;
while(stk.size()&&stk.top()>postorder[i]) {
root=stk.top();
stk.pop();
}
stk.push(postorder[i]);
}
return true;
}
};
时间复杂度 O(n)
空间复杂度 O(n)