面经之树问题

今天 某为 给我来了一个电话面试,最后在写出算法的时候,来了一个这样的题:给定一个序列,判断其是否是二叉排序树的前序遍历结果

作者回答:容我好好想想,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);
}

后面又看到了更加简短的代码,于是乎学习了一波:

算法流程如下:

  1. 创建空栈
  2. 初始化根的下界low = INI_MIN
  3. 对于数组中每个数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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值