输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
思路
- 后序遍历性质: [ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
- 二叉搜索树性质: 左子树任意节点的值 < 根节点的值;右子树任意节点的值 > 根节点的值。
方法一:递归分治
子树递推性质: 对于 [ 左子树 | 右子树 | 根节点 ] 中的左(右)子树,仍满足后序遍历和二叉搜索树性质。
因此,可以通过递归判断每个子树的 正确性 (即其后序遍历是否满足二叉搜索树性质) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。
-
终止条件: 当 i≥ji \geq ji≥j ,说明子树节点少于或等于 111 个,无需判别正确性,因此直接返回 truetruetrue ;
-
递推工作:
- 划分左右子树: 遍历后序遍历的 [i,j][i, j][i,j] 区间元素,寻找 第一个大于根节点(即 postorder[j]postorder[j]postorder[j] )的节点,索引记为 mmm 。此时,可划分出左子树区间 [i,m−1][i,m-1][i,m−1] 、右子树区间 [m,j−1][m, j - 1][m,j−1] 、根节点索引 jjj 。
- 判断是否满足二叉搜索树性质:
- 右子树区间 [m,j−1][m, j-1][m,j−1] 内的所有元素都应 >>> postorder[j]postorder[j]postorder[j] 。实现方式为遍历,当遇到 ≤postorder[j]\leq postorder[j]≤postorder[j] 的元素则跳出;后续可通过 l=jl = jl=j 判断是否满足二叉搜索树性质。
- 左子树区间 [i,m−1][i, m - 1][i,m−1] 内的所有元素都应 <<< postorder[j]postorder[j]postorder[j] 。而第
1.划分左右子树
步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
-
返回值: 所有子树都需正确才可判定正确,因此使用 与逻辑符 and 连接。
- l==jl == jl==j : 判断此树是否正确。
- recur(i,m−1)recur(i, m - 1)recur(i,m−1) : 判断 此树的左子树 是否正确。
- recur(m,j−1)recur(m, j - 1)recur(m,j−1) : 判断 此树的右子树 是否正确。
代码:
class Solution:
def verifyPostorder(self, postorder: [int]) -> bool:
def recur(i, j):
if i >= j:
return True
l = i
while postorder[l] < postorder[j]:
l += 1
m = l
while postorder[l] > postorder[j]:
l += 1
return l == j and recur(i, m - 1) and recur(m, j - 1)
return recur(0, len(postorder) - 1)
复杂度分析:
- 时间复杂度 O(N2)O(N^2)O(N2) : 每次调用 recur(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) : 最差情况下(即当树退化为链表),递归深度将达到 NNN 。