方法一:Divide and Conquer
数组第一个一定是根,第一个大于根的一定是右子树的根,因此我们可以把 根,左右子树都区分开来。判断根是否在合理范围内,如果在,在分别判断左右子树。思路清晰,也很容易写。
Time: O(nlogn)
class Solution { public: bool verifyPreorder(vector<int>& preorder) { return helper(preorder,0,preorder.size()-1,INT_MIN,INT_MAX); } bool helper(vector<int> &preorder, int start, int end, int lower, int upper){ //[start,end] if (start>end) return true; int root_val=preorder[start]; if (!(root_val>lower && root_val<upper)) return false; int i; for (i=start+1;i<=end;++i){ if (preorder[i]>root_val) break; } // [start+1,i-1] and [i,end] return helper(preorder,start+1,i-1,lower,root_val) && helper(preorder,i,end,root_val,upper); } };
方法二:Stack
维护一个单调递减的栈。如果当前元素比栈顶元素小,说明是左子树,压栈;否则是右子树,此时 lower_bound 要更新,因为后面的元素一定比出栈元素大了。
下面这张图可以帮助理解。时间复杂度和空间复杂度都为O(n)
class Solution { public: bool verifyPreorder(vector<int>& preorder) { int lower=INT_MIN; stack<int> s; for (int x:preorder){ if (x<=lower) return false; while (!s.empty() && x>s.top()){ lower = s.top(); s.pop(); } s.push(x); } return true; } };