题目描述
给出一个先序/后序序列,判断这个序列是不是某个BST树的先序/后序序列
算法原理
核心的思路就是知道当前子树的一个端点(因为是先序/后序序列,所以一个端点肯定是根节点root)和当前子树在序列中的另一个端点(可以记做tail),然后再当前树判断是不是BST树,然后再递归进入当前树的左右子树,再判断左右子树是不是BST树,如果这个序列对应的所有子树都为BST树,说明这个树也为BST树
举例用树
root | tail | ||||||
先序序列 | 8 | 6 | 5 | 7 | 10 | 8 | 11 |
tail | root | ||||||
后序序列 | 5 | 7 | 6 | 8 | 11 | 10 | 8 |
问题1:就到了如何判断一当前树是不是为BST树呢?
其实只需要判断当前节点作为根节点左子树的所有端点是不是小于当前根节点,右子树的所有节点是不是大于等于当前根节点,只需要满足这棵树的所有节点都是左孩子小于这个节点,右孩子大于等于这个节点就可以了
问题2:具体怎么判断是不是当前节点的左子树是不是全小于当前节点,右子树全部大于等于当前节点呢?
对于先序序列来说,因为是根节点——左子树——右子树,如果满足左子树都小于当前节点,右子树全部大于等于当前节点,就可以划分为根节点——小于根节点——大于等于根节点,因为序列是连续的,所以就可以转变为判断一个序列是不是先小于根节点,再大于等于根节点了,可以设置一个变量p,等于左子树在序列中出现的第一个节点(也就是root+1),先通过while循环找到第一个大于等于根节点的节点,在通过一个while循环判断剩下的值是不是都大于等于根节点,如果最后这个变量p完整的走完了这个序列(也就是说明指向了tail+1),就说明满足左子树小于当前节点,右子树大于等于当前节点。
用代码解释就是
int p=root+1;
while(p<=tail&&pre[p]<pre[root]) p++; //先判断小于根结点的
m=p; //m是用作记录,用于递归左右子树的判断
while(p<=tail&&pre[p]>=pre[root]) p++; //在判断大于等于根结点的
if(p!=tail+1) return ; //如果能走到序列最后就满足
//如果不能走到序列最后就说明不满足,直接返回
对于后序序列来说,因为最后一个节点是根节点,所以只需要判断从另一个端点tail开始,到root前面那个节点是不是满足条件就可以了
代码解释
int p=begin;
while(postorder[p]<postorder[root]) p++;
int m=p;
while(postorder[p]>=postorder[root]&&p<root) p++;
对于先序序列,判断的范围是[root+1,tail]
对于后序序列,判断的范围是[tail,root-1]
root | tail | ||||||
先序序列 | 8 | 6 | 5 | 7 | 10 | 8 | 11 |
tail | root | ||||||
后序序列 | 5 | 7 | 6 | 8 | 11 | 10 | 8 |
问题3:知道当前节点判断原理了,如何判断递归进左右子树呢?
对于一个已经判断完是不是满足的节点后,我们就可以利用一个变量m来记录p代表的第一个大于根节点的节点(也就是第一个while循环后),这个节点m就是右子树的第一个节点,同时也是右子树的根节点,m-1就是左子树的最后一个节点,
注意:子树的在先序序列中的第一个节点是root,最后一个节点时tail,
在后序序列中第一个节点时tail,最后一个节点时root
所以对于先序序列来说,左子树的根节点为root+1,左子树在序列中的最后一个位置为m-1
右子树的根节点为m,右子树在序列中的最后一个位置为tail
postOrder(root+1,m-1); //左子树
postOrder(m,tail); //右子树
对于后序序列来说,左子树的根节点为m-1,左子树的第一个节点为tail
右子树的根节点为root-1,右子树的第一个节点为m
judge(tail,m-1,postorder) //左子树
judge(m,root-1,postorder) //右子树
对应序列
root | root+1 | m-1 | m | tail | |||
根节点 | 左子树 | 右子树 | |||||
先序序列 | 8 | 6 | 5 | 7 | 10 | 8 | 11 |
tail | m-1 | m | root-1 | root | |||
左子树 | 右子树 | 根节点 | |||||
后序序列 | 5 | 7 | 6 | 8 | 11 | 10 | 8 |
问题4:递归结束的条件是什么呢?
很显然,当到达叶子结点,只有一个节点的时候肯定是BST树了嘛,当root==tail的时候就说明到达叶子节点了,直接返回就可以了
注意:当先序序列求后序序列,后序序列求先序序列,在叶子结点的时候也要讲叶子结点放入要求的数组中,然后再两次递归结束后也需要将结点放入其中
核心代码实现
对于先序序列,如果还要求出对应的后序序列,有
void postOrder(int root,int tail){
if(root>tail) return ;
if(root==tail){
post.push_back(pre[root]);
return ;
}
int i=root+1,j=tail;
int p=root+1;
int m;
while(p<=tail&&pre[p]<pre[root]) p++;
m=p;
while(p<=tail&&pre[p]>=pre[root]) p++;
if(p!=tail+1) return ;
postOrder(root+1,m-1); //左子树
postOrder(m,tail); //右子树
post.push_back(pre[root]);
}
对于后序序列有
bool judge(int begin,int root,vector<int>& postorder){
if(begin>=root) return true;
int p=begin;
while(postorder[p]<postorder[root]) p++;
int m=p;
while(postorder[p]>=postorder[root]&&p<root) p++;
return root==p&&judge(begin,m-1,postorder)&&judge(m,root-1,postorder);
}
例题
PAT甲: 1043 Is It a Binary Search Tree(前序中序转后序)(两种解法)LeetCode: 剑指 Offer 33. 二叉搜索树的后序遍历序列PAT甲: 1043 Is It a Binary Search Tree(前序中序转后序)(两种解法)