题解 | #二叉搜索树的后序遍历序列#
二叉搜索树的后序遍历序列
http://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd
解题思路:
1、根据二叉搜索树的后续遍历规律:左子树--右子树--根;序列最后一个元素为根节点,左子树的元素值都小于根节点,右子树的元素值都大于根节点。
(1)将数组的元素分为两部分:左子树序列值和右子树序列值,左子树值都小于根节点值,右子树值都大于根节点值
(2)分别对左右子树序列值进行(1)方法递归,如果递归过程中发现其右子树序列中有值小于根节点值,则不是一个后续序列
2、采用单调栈的形式解题
(1)初始化: 单调栈 stack ,父节点值 root(初始值为正无穷大,可把树的根节点看为此无穷大节点的左孩子);
(2)倒序遍历数组:记每个节点为 ri ,判断: 若 ri > root ,说明此后序遍历序列不满足二叉搜索树定义,直接返回false ;
(3)更新父节点 root: 当栈不为空 且 ri < stack.peek() 时,循环执行出栈,并将出栈节点赋给 root 。
(4)入栈: 将当前节点 ri 入栈;
(5)若遍历完成,则说明后序遍历满足二叉搜索树定义,返回 true。
(2)倒序遍历数组:记每个节点为 ri ,判断: 若 ri > root ,说明此后序遍历序列不满足二叉搜索树定义,直接返回false ;
(3)更新父节点 root: 当栈不为空 且 ri < stack.peek() 时,循环执行出栈,并将出栈节点赋给 root 。
(4)入栈: 将当前节点 ri 入栈;
(5)若遍历完成,则说明后序遍历满足二叉搜索树定义,返回 true。
举例说明:
数组:【4,8,6,12,16,14,10】
(1)根节点为10,左子树为【4,8,6】,右子树为【12,16,14】
(2)分别对左右子树进行递归;针对左子树时,左子树的根节点为6,其左子树为【4】,其右子树为【8】,则该左子树符合后续遍历规则
(3)针对右子树时,左子树的根节点为14,其左子树为【12】,其右子树为【16】,则该右子树也符合后续遍历规则,则该数组是二叉搜索树的后序遍历序列
图表展示:
步骤 | 数组序列 | 根节点 | 左子树 | 右子树 |
1 | 【4,8,6,12,16,14,10】 | 【10】 | 【4,8,6】 | 【12,16,14】 |
2 | 【4,8,6】 | 【6】 | 【4】 | 【8】 |
3 | 【12,16,14】 | 【14】 | 【12】 | 【16】 |
根据图表步骤展示,该数组的左右子树都符合二叉搜索树的后序遍历规则,因此该数组 是二叉搜索树的后序遍历序列 |
代码展示:
Python版本:
# -*- coding:utf-8 -*- class Solution: def VerifySquenceOfBST(self, sequence): # write code here if not sequence: return False # 递归 return self.check(sequence, 0, len(sequence) - 1) def check(self, sequence, left, right): if left >= right: return True # 找到第一个大于根节点的元素 mid = left root = sequence[right] while sequence[mid] < root: mid += 1 tmp = mid # 保障postorder[mid]后的数要大于root,否则返回false while tmp < right: tmp += 1 if sequence[tmp] < root: return False # 递归左右子树进行判断 return self.check(sequence, left, mid - 1) and self.check(sequence, mid, right - 1)
public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { // 判断是否师空序列 if (sequence.length == 0){ return false; } return dfs(sequence, 0, sequence.length); } private boolean dfs(int[] sequence, int start, int end) { if (start >= end - 1) return true; // 根结点是最后一个元素 int root = sequence[end - 1]; // 左子树都比根结点小,右子树都比根结点大 int i = start; while (i < end && sequence[i] < root) // i 为左右子树的边界 ++i; // 判断右子树是否比根节点大 for (int j = i; j < end; ++j) if (sequence[j] < root) return false; // 对序列的前后两部分进行递归调用 return dfs(sequence, start, i) && dfs(sequence, i, end - 1); } }
复杂度分析:
时间复杂度 O(N^2): 每次调用 递归算法减去一个根节点,因此递归占用 O(N) ;最差情况下(即当树退化为链表),每轮递归都需遍历树所有节点,占用 O(N) 。
空间复杂度 O(N): 最差情况下(即当树退化为链表),递归深度将达到 N
空间复杂度 O(N): 最差情况下(即当树退化为链表),递归深度将达到 N