33 二叉搜索树的后序遍历序列(举例让抽象问题具体化)
题目描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
注意:定义上说空树是二叉搜索树,但是出题者在测试用例上认为空树不是二叉搜索树,测试答案写的是false
测试用例:
解题思路:
1)处理二叉树的遍历,可以先找到二叉树的根节点,再基于根节点把整棵树的遍历序列拆分成左子树对应的子序列和右子树对应的子序列,然后递归的处理这两个子序列
实现一:传入子序列时,使用迭代器赋值,该操作在数据量很大时,非常耗时。
class Solution { public: bool VerifySquenceOfBST(vector<int> sequence) { if(sequence.empty()) return false; int length = sequence.size(); int root = sequence[length-1]; //根节点 int i=0; //寻找左右子树的分割位置 for(;i<length-1;++i){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[i]>root) break; } int j=i; //判断右子树中是否有小于root节点的值,有则不是二叉搜索树的后序遍历 for(;j<length-1;++j){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[j]<root) return false; } bool left = true; //左子树为空时,返回true if(i>0){ vector<int> leftSeq(sequence.begin(),sequence.begin()+i); //迭代器end,指向最后一个元素的后一位,即[begin,end) 半开半闭区间 left = VerifySquenceOfBST(leftSeq); } bool right = true; if(i<length-1){ //是length-1 vector<int> rightSeq(sequence.begin()+i,sequence.end()-1); //减去根节点 right = VerifySquenceOfBST(rightSeq); } return (left && right); } };
实现2:对sequence取引用,用索引控制搜索的空间
错误的代码,子序列不能以0开始,应该从frontIndex开始。
//该段代码有误,我认为是错误的,为什么能通过,原理是什么???
class Solution { public: bool VerifySquenceOfBST(vector<int> sequence) { if(sequence.empty()) return false; return VerifySquenceOfBSTCore ( sequence, 0,sequence.size()-1); } //sequence是否改变???不改变代码应该是错误的,为什么能通过 bool VerifySquenceOfBSTCore (vector<int> &sequence, int frontIndex,int backIndex) { int length = backIndex - frontIndex + 1; //不能使用sequence.size()一直不变 //if(length==1) return true; if(length<0) //此段代码不添加可以通过,添加也可以通过。 但若是添加if(length<=0),则会报错。 return false; int root = sequence[length-1]; //根节点 int i=0; //寻找左右子树的分割位置 for(;i<length-1;++i){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[i]>root) break; } int j=i; //判断右子树中是否有小于root节点的值,有则不是二叉搜索树的后序遍历 for(;j<length-1;++j){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[j]<root) return false; } bool left = true; //左子树为空时,返回true if(i>0){ left = VerifySquenceOfBSTCore(sequence,0,i-1); //调用时,不会出现length=0的情况,因为i-1>=0,至少有一个元素 } bool right = true; if(i<length-1){ //是length-1 right = VerifySquenceOfBSTCore(sequence,j,length-1-1); } return (left && right); } };
正确的代码:
class Solution { public: bool VerifySquenceOfBST(vector<int> sequence) { if(sequence.empty()) return false; return VerifySquenceOfBSTCore ( sequence, 0,sequence.size()-1); } //sequence是否改变???不改变代码应该是错误的,为什么能通过 bool VerifySquenceOfBSTCore (vector<int> &sequence, int frontIndex,int backIndex) { int length = backIndex - frontIndex + 1; //不能使用sequence.size()一直不变 //if(length==1) return true; //if(length<=0) 递归时的if判断,保证了不会传入length<=0的情况。 //return false; int root = sequence[backIndex]; //根节点 int i=frontIndex; //寻找左右子树的分割位置 for(;i<backIndex;++i){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[i]>root) break; } int j=i; //判断右子树中是否有小于root节点的值,有则不是二叉搜索树的后序遍历 for(;j<backIndex;++j){ //注:不是<length 而是<length-1,排除root节点。 if(sequence[j]<root) return false; } bool left = true; //左子树为空时,返回true if(i>frontIndex){ left = VerifySquenceOfBSTCore(sequence,frontIndex,i-1); //调用时,不会出现length=0的情况,因为i-1>=0,至少有一个元素 } bool right = true; if(i<backIndex){ //是length-1 right = VerifySquenceOfBSTCore(sequence,i,backIndex-1); } return (left && right); } };
2)非递归方法:
非递归 (也是一个基于递归的思想) 左子树一定比右子树小,因此去掉根后,数字分为left,right两部分 right部分的最后一个数字是右子树的根他也比左子树所有值大,因此我们可以每次只看右子树是否符合条件即可,即使到达了左子树,左子树也可以看成由左右子树组成的树。还向右子树那样处理 对于左子树回到了原问题,对于右子树,左子树的所有值都比右子树的根小可以暂时把他看出右子树的左子树,只需看看右子树的右子树是否符合要求即可
class Solution { public: bool VerifySquenceOfBST(vector<int> sequence) { int size = sequence.size(); if(0==size)return false; int i = 0; while(--size) { //while(sequence[i++]<sequence[size]); while(sequence[i]<sequence[size])i++; //while(sequence[i++]>sequence[size]); while(sequence[i]>sequence[size])i++; if(i<size)return false; i=0; } return true; } };
这种写法多次重复搜索了左子树,并不是好的做法。非递归版本的最好和最坏时间开销都是O(n2),递归版本的最好时间开销是O(n*log2n),最差时间开销是O(n2)
从最后每次向前一个,就是其右子树根,其大于所有原来的左子树和自己的左子树,巧的是后序遍历这两个也都放到前面,而大的右子树在后面,但这个非递归貌似只适用这个题,换个中序遍历好像就不可以了
{4,6,5,9,11,8,7} 可以是二叉搜索树的后序遍历。当9是11的左子树,11是8的右子树时。