二叉搜索树的后序遍历序列
题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同
思路:
这个题有两种解法,先介绍第一种方法
我们知道后序遍历是 左右根的顺序,所以数组的最后一个元素就是根。
二叉搜索树的特点是,左子树的值小于根,右子树的值大于根。
所以需要在数组中,先找到小于根结点的一部分结点,记住分界点m.【start,m-1】]是左子树数组。【m,end-1】就是右子树,遍历右子树数组中的值,如果有小于根结点的,返回FALSE。
左右子树分别递归查找,只有左子树和右子树同时满足是二叉树,整棵树才是二叉搜索树。
代码如下:
bool verifyPostorder(vector<int>& postorder) {
/*
数组最后一个元素的前一个元素如果小于当前值,则是左子树上的值,大于当前值就是右子树结点值
不断递归循环判断。
如果左子树上出现比根结点还大的值,就不是二叉搜索树
同理,如果右子树上出现比根结点还小的值,也不是二叉搜索树
*/
if(postorder.size()==0) return true;
return help(postorder,0,postorder.size()-1);
}
bool help(vector<int>& postorder,int start,int end){
if(start == end) return true;
//根结点
// int len = postorder.size();
int root = postorder[end];
//在二叉搜索树中左子树结点的值小于根结点的值
int i=start;
for( ; i<end; i++){
if(postorder[i] > root){
break;
}
}
//在二叉搜索树中右子树结点的值大于根结点的值
int j=i;
for(; j<end; j++){
if(postorder[j] < root)
return false;
}
bool left=true;
bool right = true;
//判断左右子树是不是二叉搜索树
if(i>start){ //左子树不为空
left = help(postorder,start,i-1);
}
if(i<end){ //右子树不为空
right = help(postorder,i,end-1);
}
return (left && right);
}
第二种方法:
最巧妙的是用单调栈的方法,访问后序遍历序列的倒序数组,变成了根右左的顺序,即先遍历根然后是右子树,最后是左子树。
构造单调递增栈,不断向右遍历,遍历到的值大于栈顶元素的值,则代表当前结点是右结点,则压入栈中。直到遇到一个小于当前栈顶元素的值,说明遍历到了左子树,需要在栈中找到当前左子树的父节点。怎么找呢? 如果栈不为空,就将栈顶值大于当前值的元素弹出来,直到栈顶元素小于当前值了(或者栈为空了),停止pop。并记录最后一次弹出的元素,即当前左子树的父节点,接下来的结点值都应该小于该父节点值。如果不满足,就返回false,若能顺利遍历完整个数组,则说明该数组满足条件!
代码如下:
bool verifyPostorder(vector<int>& postorder) {
//用单调栈的方法来做,时间和空间复杂度都是O(N)
if(postorder.size()==0) return true; //当是空树时,默认也是正确的
stack<int> st;
//定义前一个父节点的值
int preEle = INT_MAX;
//用后续遍历的倒序数组来做,确认是单调的
for(int i=postorder.size()-1; i>=0; i--){
if(postorder[i] > preEle){
return false; //如果当前元素大于根结点,肯定就是错的
}
// 当当前节点的值小于父节点的值,说明遍历到了左子树上,所以要找到这个左子树 的父节点
//在栈中找到最后一个大于当前值的结点并记录下来
while(!st.empty() && postorder[i] < st.top()){
preEle = st.top();
st.pop();
}
//如果,当前元素大于根结点的值 或者 栈为空, 将当前数据压入栈中
st.push(postorder[i]);
}
//如果能遍历完整个数组,就是正确的
return true;
}