【剑指offer-23】20190812/02 二叉搜索树的后序遍历序列

【剑指offer-23】二叉搜索树的后序遍历序列

  • 考点:栈 树
  • 时间限制:1秒
  • 空间限制:32768K
  • 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

思路:
  • 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

采用分治法的思想,找到根结点、左子树的序列、右子树的序列,分别判断左右子序列是否为二叉树的后序序列。

由题意可得:

  1. 后序遍历序列的最后一个元素为二叉树的根节点
  2. 二叉搜索树左子树上所有的结点均小于根结点、右子树所有的结点均大于根结点。

算法步骤如下:

  1. 找到根结点
  2. 遍历序列,找到第一个大于等于根结点的元素i,则i左侧为左子树、i右侧为右子树
  3. 我们已经知道i左侧所有元素均小于根结点,那么再依次遍历右侧,看是否所有元素均大于根结点;若出现小于根结点的元素,则直接返回false;若右侧全都大于根结点,则:
  4. 分别递归判断左/右子序列是否为后序序列
代码:
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if (sequence.length == 0) {
            return false;
        }
        return VerifySquenceOfBST1(sequence, 0, sequence.length - 1);
    }
    private boolean VerifySquenceOfBST1(int [] sequence, int start, int end) {
        // 已经拆分到最小不可以在拆分了
        if (start >= end) {
            return true;
        }
        
        // 找到根节点
        int root = sequence[end];
        
        // 寻找左右子树 加入判断i<end
        int i = start;
        while (sequence[i] < root && i < end) {
            i++;
        }
        
        // j后面都是右子树了,检查是不是右边都大于根节点的数值
        int j = i;
        while (j <= end) {
            if (sequence[j] < root) {
                return false;
            }
            j++;
        }
        // 检查左子树
        boolean left = VerifySquenceOfBST1(sequence, start, i - 1);
        boolean right = VerifySquenceOfBST1(sequence, i, end - 1);
        return left && right;
    }
}
我的问题:
  1. 没有搞清楚二叉搜索树(BST)的概念。导致看到题目完全没有思路,不知道怎么解决,只知道把后序遍历写出来。
  2. 在参考了思路之后,还是有些地方写错了。
    1. 对于sequence数组为空的情况,那么必然不是BST的后序遍历结果,记得算法一开始要检查这种情况,返回false。
    2. 在寻找左右子树分割的结点的节点的时候,找到的第一个大于根节点的树,属于右子树。并且要注意,在寻找这个节点的时候,i的值一定是要小于end。(要防止i的值越界)。
    3. 在进行后面的检查右子树是不是都大于根节点的时候就比较简单了,循环遍历条件是,j<=end,也就是从这个点到最后一个点end,逐个检查。如果有不符合的,返回false。
    4. 接下来分治也很重要。检查从start,到i - 1,也就是左子树的范围。其次是右子树,从i到end - 1(也就是从i开始,到最后一个节点的前一个节点。(最后一个节点是后序遍历的根节点)。分而治之搜索。
    5. 最终返回两个结果的与&&的结果。得到答案。

其他思路1:

一个非常规方法,看了半天没看到一样的,提供一个新思路。
前面有道题是“栈的压入、弹出序列”。
写这道题的例子时发现二叉树的中序序列和后序序列就满足栈的压入弹出序列关系。
即如果把中序序列当做栈的压入序列,那么后序序列是该栈的一个弹出序列。
而BST的中序是排序数组。因此将本题的序列排序作为中序序列,引用前面题的答案判断两序列是否满足上述关系即可。

我想了半天没想明白,原来是这个意思呀。
就是中序序列,可以满足栈的压入顺序,而后序序列,就是栈的弹出序列。
如果我们把传进来的数组,排序,那他就是栈的压入序列了。然后用之前的栈的压入弹出算法进行解答这道题,后序序列是满足的弹出序列的话,那么这就是一个正确的后序序列。
而且对于BST来说,中序序列就是有序的二叉树。

import java.util.Stack;
import java.util.Arrays;

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        // 克隆数组,排序序列,找到中序序列的入栈顺序
        int[] arr = sequence.clone();
        Arrays.sort(arr);
        return IsPopOrder(arr, sequence);
    }
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        if (pushA.length == 0 || popA.length == 0) {
            return false;
        }
        Stack<Integer> s = new Stack<Integer>();
        // 标识弹出序列的index
        int popIndex = 0;
        for (int i = 0; i < pushA.length; i++) {
            s.push(pushA[i]);
            while (!s.empty() && popA[popIndex] == s.peek()) {
                popIndex++;
                s.pop();
            }
        }
         return s.empty();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值