Leetcode刷题笔记——剑指 Offer 33. 二叉搜索树的后序遍历序列(中等)

文章目录

  • 题目描述
  • 方法一:递归分治
    • 复杂度分析
    • C++代码实现
  • 方法二:辅助单调栈
    • 复杂度分析
    • C++代码实现


题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

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

方法一:递归分治

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

复杂度分析

时间复杂度 O ( N 2 ) O(N^2) O(N2) : 每次调用 r e c u r ( i , j ) recur(i,j) recur(i,j) 减去一个根节点,因此递归占用 O ( N ) O(N) O(N) ;最差情况下(即当树退化为链表),每轮递归都需遍历树所有节点,占用 O ( N ) O(N) O(N)

空间复杂度 O ( N ) O(N) O(N) : 最差情况下(即当树退化为链表),递归深度将达到 N N N

C++代码实现

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        return recur(postorder, 0, postorder.size()-1);
    }
    bool recur(vector<int>& postorder,int i, int j){
        if(i >= j)return true;
        int p = i;
        // 寻找第一个比根节点大的元素
        while(postorder[p] < postorder[j]) p++;
        // 标记该元素的序号
        int m = p;
        // 验证右子树区间元素是否大于根节点
        while(postorder[p] > postorder[j]) p++;
        return p == j && recur(postorder, i, m-1) && recur(postorder, m, j-1);
    }
};

方法二:辅助单调栈

  1. 后序遍历倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。
    Leetcode刷题笔记——剑指 Offer 33. 二叉搜索树的后序遍历序列(中等)_第1张图片

  2. 设后序遍历倒序列表为 [ r n , r n − 1 , . . . , r 1 ] [r_{n}, r_{n-1},...,r_1] [rn,rn1,...,r1],遍历此列表,设索引为 i i i ,若为二叉搜索树 ,则有:
    当节点值 r i > r i + 1 r_i > r_{i+1} ri>ri+1时: 节点 r i r_i ri 一定是节点 r i + 1 r_{i+1} ri+1的右子节点。
    当节点值 r i < r i + 1 r_i < r_{i+1} ri<ri+1时: 节点 r i r_i ri 一定是某节点 root 的左子节点,且 root 为节点 r i + 1 , r i + 2 , . . . , r n r_{i+1}, r_{i+2},..., r_{n} ri+1,ri+2,...,rn中值大于且最接近 r i r_i ri的节点(∵ root 直接连接左子节点 r i r_i ri)。

  3. 当遍历时遇到递减节点 r i < r i + 1 r_i < r_{i+1} ri<ri+1,若为二叉搜索树,则对于后序遍历中节点 r i r_i ri 右边的任意节点 r x ∈ [ r i − 1 , r i − 2 , . . . , r 1 ] r_x \in [r_{i-1}, r_{i-2}, ..., r_1] rx[ri1,ri2,...,r1],必有节点值 r x < r o o t r_x < root rx<root

注:节点 r x r_x rx 只可能为以下两种情况:① r x r_x rx r i r_i ri的左、右子树的各节点;② r x r_x rx r o o t root root 的父节点或更高层父节点的左子树的各节点。在二叉搜索树中,以上节点都应小于 r o o t root root
Leetcode刷题笔记——剑指 Offer 33. 二叉搜索树的后序遍历序列(中等)_第2张图片
遍历 “后序遍历的倒序” 会多次遇到递减节点 r i r_i ri,若所有的递减节点 r i r_i ri 对应的父节点 r o o t root root 都满足以上条件,则可判定为二叉搜索树。
根据以上特点,考虑借助单调栈实现:

  1. 借助一个单调栈 stack 存储值递增的节点;
  2. 每当遇到值递减的节点 r i r_i ri,则通过出栈来更新节点 r i r_i ri 的父节点 r o o t root root
  3. 每轮判断 r i r_i ri r o o t root root的值关系:
    1. r i > r o o t r_i > root ri>root则说明不满足二叉搜索树定义,直接返回 f a l s e false false
    2. r i < r o o t r_i < root ri<root则说明满足二叉搜索树定义,则继续遍历。

算法流程:

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

复杂度分析

时间复杂度 O ( N ) O(N) O(N): 遍历 p o s t o r d e r postorder postorder 所有节点,各节点均入栈 / 出栈一次,使用 O ( N ) O(N) O(N) 时间。
空间复杂度 O ( N ) O(N) O(N): 最差情况下,单调栈 s t a c k stack stack 存储所有节点,使用 O ( N ) O(N) O(N) 额外空间。

C++代码实现

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        stack<int> stack;
        int root = INT_MAX;
        for(int i = postorder.size()-1; i >= 0; i--){
            if(postorder[i] > root) return false;
            while(!stack.empty() && stack.top() > postorder[i]){
                root = stack.top();
                stack.pop();
            }
            stack.push(postorder[i]);
        }
        return true;
    }
};

你可能感兴趣的:(Leetcode刷题,leetcode,算法,职场和发展)