今天 某为 给我来了一个电话面试,最后在写出算法的时候,来了一个这样的题:给定一个序列,判断其是否是二叉排序树的前序遍历结果
作者回答:容我好好想想,em.........
PS:又是二叉排序树,你确定知道二叉排序树的特性吗?
算法思想:二叉排序树的左子树的结点的值均小于根节点的值,右子树结点的值均大于根节点的值,借助于这个想法,我们可以判断给出的序列是否是BST的先序遍历结果
bool VerifyPreSquenceOfBST(int sequence[], int length){
if(sequence == NULL || length <= 0)
return false;
if (length==1)
return true;
int root = sequence[0];
// 在二叉搜索树中左子树的结点小于根结点
int i = 1;
for(; i < length; ++ i){
if(sequence[i] > root)
break;
}
// 在二叉搜索树中右子树的结点大于根结点
int j = i;
for(; j < length; ++ j) {
if(sequence[j] < root)
return false;
}
// 判断左子树是不是二叉搜索树
bool left = true;
if(i > 1)
left = VerifyPreSquenceOfBST(sequence+1, i-1);//注意细节
// 判断右子树是不是二叉搜索树
bool right = true;
if(i < length)
right = VerifyPreSquenceOfBST(sequence + i, length - i);//注意细节
return (left && right);
}
后面又看到了更加简短的代码,于是乎学习了一波:
算法流程如下:
- 创建空栈
- 初始化根的下界low = INI_MIN
- 对于数组中每个数pre[i]
a)如果pre[i] 小于当前下界low,return false
b)如果pre[i] > 栈顶元素则持续弹出栈顶元素,最后一个弹出的为当前子树的根,因此将low更新为pre[i]。
c)将pre[i]进栈(栈中元素保持递减性质)
给出一个二叉排序树,然后按照这个算法过程模拟如下:
4
/ \
2 5
/ \ \
1 3 6
这棵树是一颗二叉排序树,判断序列[4,2,1,3,5,6]是否可能是二叉排序树的先序序列,模拟如下
1.首先对于元素4,会运行path.emplace(p),将p加入到path中;
2.对于元素2,因为2<4,所以仍然是插入元素2;
3.对于元素1,同样插入到path中,此时path=[4,2,1]
4.对于元素3,此时会进入到while循环
a)更新low=1,弹出元素1,
b)p=3,path.top=2,此时仍然进行循环,low=2,弹出元素2
c)p=3,path.top=4,不满足循环条件,跳出
5.插入元素3;
6.对于元素5,此时同步骤4,最后path=[5]
7.对于元素6,弹出元素5中,low=5,此时,path=[6]
8.所有的元素都已经遍历完成,是时候返回True了。
bool verifyPreorder(vector<int>& preorder) {
int low = INT_MIN;
stack<int> path;
for (auto& p : preorder) {
if (p < low) {
return false;
}
while (!path.empty() && p > path.top()) {
low = path.top();
path.pop();
}
// 插入p元素
path.emplace(p);
}
return true;
}
PS:面试官顺带的提了一句,你知道用前序和中序序列构建二叉树么?(因为笔者在手撕上面这个代码的时候中断了好一会儿......)
同样的,可以给出这个代码学习一番:
算法过程如下:
1.根据前序遍历的特点,第一个结点就是根节点
2.在中序遍历序列中找到根节点的位置,根节点将中序序列分成两个部分,左边的是根节点的左子树,右边是根节点的右子树
3.按照结点个数关系,然后重复步骤1-2(递归),直到每棵子树仅有一个节点为止,这样就可以建立出一棵二叉树。
//已知先序和中序序列,构建二叉树
BiTree PreInCreate(int A[],int B[],int l1,int h1,int l2,int h2){
//l1,h1为先序的第一个结点和最后一个节点的下标,l2,h2为中序的第一和最后一个节点的下标
root=(BiTNode *)malloc(sizeof(BiTNode));
root->data=A[l1];
for(i=l2;B[i]!=root->data;i++); //根节点在中序序列的划分
llen=i-l2;
rlen=h2-i;
if(llen)
root->lchild=PreInCreate(A,B,l1+1,l1+llen,l2,l2+llen-1);
else
root->lchild=NULL;
if(rlen)
root->rchild=PreInCreate(A,B,h1-rlen+1,h1,h2-rlen+1,h2);
else
root->rchild=NULL;
return root;
}
PS:小小的变化一下,那么已知中序和后序,你能不能快速的建立一棵二叉树呢?
面试拓展:给定一个序列,判断其是否是BST的后序遍历结果?(剑指offer)
算法思想:可以将输入序列划分为3部分,即left、right、root,首先找到left部分最后一个结点的下标,即可完成分隔。如果left部分和right部分均是BST,即可递归调用VerifySquenceOfBST( )函数,变量bleft记录left部分是否为BST,bright记录right部分是否为BST。i从0~len-1对所有结点遍历一次... 最后的bleft&&bright即为所求的值。
public boolean isAfterTree(int[] sequence,int length){
int len = sequence.length;
if(sequence==null || length<=0)
return false;
int i=0;
int root = sequence[length-1];
for(;i<length-1;i++){
if(sequence[i]>root){
break; //得到左子树 i为大于root的点的下标
}
}
int j=i; //右子树起始点
for(;j<length-1;j++){
if(sequence[j]<root)
return false; //若发现右子树中有小于根节点的值,则不合法
}
boolean bleft = true;
if(i>0)
bleft = isAfterTree(Arrays.copyOfRange(sequence, 0, i),i);
boolean bright = true;
if(j<len-1)
bright = isAfterTree(Arrays.copyOfRange(sequence, i, j),length-i);
return (bleft&&bright);
}
参考解答:https://blog.csdn.net/gogokongyin/article/details/51622706