剑指--二叉搜索树的后序遍历序列

剑指–二叉搜索树的后序遍历序列

1,题目:

在这里插入图片描述
在这里插入图片描述
2,思路:

方法一:递归分治:

解题思路:

  • 后序遍历定义: [ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
  • 二叉搜索树定义: 左子树中所有节点的值 < 根节点的值;右子树中所有节点的值 > 根节点的值;其左、右子树也分别为二叉搜索树。

递归解析:

  • 1.终止条件: 当 i≥j ,说明此子树节点数量 ≤1 ,无需判别正确性,因此直接返回 true ;
  • 2.递推工作:
  • 3.划分左右子树: 遍历后序遍历的 [i,j] 区间元素,寻找 第一个大于根节点 的节点,索引记为 m。此时,可划分出左子树区间 [i,m−1] 、右子树区间 [m,j−1] 、根节点索引 j 。
  • 4.判断是否为二叉搜索树:
  • 5.左子树区间 [i,m−1] 内的所有节点都应 <postorder[j] 。而第 1.划分左右子树 步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
  • 6.右子树区间 [m,j−1] 内的所有节点都应 >postorder[j] 。实现方式为遍历,当遇到 ≤postorder[j] 的节点则跳出;则可通过 p=j 判断是否为二叉搜索树。
  • 7.返回值: 所有子树都需正确才可判定正确,因此使用 与逻辑符 && 连接。
  • 8.p=j : 判断 此树 是否正确。
  • 9.recur(i,m−1) : 判断 此树的左子树 是否正确。
  • 10.recur(m,j−1) : 判断 此树的右子树 是否正确。

下面是对应的图解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法二:辅助单调栈:
(这个方法二如果是自己写是写不出来的)

算法流程:

  • 1.初始化: 单调栈 stack ,父节点值root=+∞ (初始值为正无穷大,可把树的根节点看为此无穷大节点的左孩子);
  • 2.倒序遍历 postorder :记每个节点为ri;
  • 3.判断: 若ri>root ,说明此后序遍历序列不满足二叉搜索树定义,直接返回 false ;
  • 4.更新父节点 root : 当栈不为空 且ri<stack.peek() 时,循环执行出栈,并将出栈节点赋给root 。
  • 5.入栈: 将当前节点ri入栈;
  • 6.若遍历完成,则说明后序遍历满足二叉搜索树定义,返回 true 。

下面是对应的图解:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3,代码:

方法一:递归分治:

class Solution {
    public boolean verifyPostorder(int[] postorder) {

        /*
        递归分治:
        解题思路:
后序遍历定义: [ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
二叉搜索树定义: 左子树中所有节点的值 << 根节点的值;右子树中所有节点的值 >> 根节点的值;其左、右子树也分别为二叉搜索树。


递归解析:
1.终止条件: 当 i≥j ,说明此子树节点数量 ≤1 ,无需判别正确性,因此直接返回 true ;
2.递推工作:
3.划分左右子树: 遍历后序遍历的 [i,j] 区间元素,寻找 第一个大于根节点 的节点,索引记为 m。此时,可划分出左子树区间 [i,m−1] 、右子树区间 [m,j−1] 、根节点索引 j 。
4.判断是否为二叉搜索树:
5.左子树区间 [i,m−1] 内的所有节点都应 <postorder[j] 。而第 1.划分左右子树 步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
6.右子树区间 [m,j−1] 内的所有节点都应 >postorder[j] 。实现方式为遍历,当遇到 ≤postorder[j] 的节点则跳出;则可通过 p=j 判断是否为二叉搜索树。
7.返回值: 所有子树都需正确才可判定正确,因此使用 与逻辑符 && 连接。
8.p=j : 判断 此树 是否正确。
9.recur(i,m−1) : 判断 此树的左子树 是否正确。
10.recur(m,j−1) : 判断 此树的右子树 是否正确。



        */
        return recur(postorder, 0, postorder.length - 1);
    }
    boolean recur(int[] postorder, int i, int j) {
        if(i >= j) return true;
        int p = i;
        while(postorder[p] < postorder[j]) p++;//这样p就指向了比根节点大的第一个节点(其实也就是右子树的第一个节点)
        int m = p;
        while(postorder[p] > postorder[j]) p++;//这时候这个循环是找的右子树,这时候p已经指向了最后的节点,也就是根节点
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);//recur(postorder, i, m - 1)这个是循环左子树了。recur(postorder, m, j - 1)这个是循环右子树了。左子树和右子树也是这样的方法。。。慢慢的会再循环更小的左子树和右子树
    }
}

方法二:辅助单调栈:

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        /*

        算法流程:
1.初始化: 单调栈 stack ,父节点值root=+∞ (初始值为正无穷大,可把树的根节点看为此无穷大节点的左孩子);
2.倒序遍历 postorder :记每个节点为ri;
3.判断: 若ri>root ,说明此后序遍历序列不满足二叉搜索树定义,直接返回 false ;
4.更新父节点 root : 当栈不为空 且ri<stack.peek() 时,循环执行出栈,并将出栈节点赋给root 。
5.入栈: 将当前节点ri入栈;
6.若遍历完成,则说明后序遍历满足二叉搜索树定义,返回 true 。

        */
        Stack<Integer> stack = new Stack<>();
        int root = Integer.MAX_VALUE;
        for(int i = postorder.length - 1; i >= 0; i--) {
            if(postorder[i] > root) //因为此时的root是无穷大,但是postorder[i] > root说明是不符合逻辑的,所以返回false
                return false;
            while(!stack.isEmpty() && stack.peek() > postorder[i])
            	root = stack.pop();//更新root节点
            stack.add(postorder[i]);
        }
        return true;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值