- 题目描述
首先这里需要知道二叉搜索树的性质的先验知识:
二叉搜索树又叫二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上的所有结点都小于根节点上的值
- 若它的右子树不为空,则右子树上的所有结点都大于根节点上的值
- 它的左右子树也分别是二叉搜索树
那么二叉搜索数的后续遍历一定有以下特性:
- 所有的左子树均小于根节点
- 所有右子树均大于根节点
- 最后一个一定是根节点
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树: 5 / 2 6 / 1 3 示例 1: 输入: [1,6,3,2,5] 输出: false 示例 2: 输入: [1,3,2,6,5] 输出: true 提示: 数组长度 <= 1000
- 题解一:递归解法参照剑指offer
思路1:自己的解法
首先需要判断左子树节点值小于根节点的值,然后判断右子树节点值大于根节点的值,然后用递归判断左子树是不是二叉搜索树,再递归判断右子树是否是二叉搜索树,最后将左右子树的判断结果bool值相与即可。
class Solution: ''' 参考剑指offer书的解法,自己的实现 ''' def verifyPostorder(self, postorder): if not postorder: return True root = postorder[len(postorder)-1] left = True for i in range(len(postorder)): if root < postorder[i]: break left_tree = postorder[:i] for j in range(i,len(postorder)-1): if postorder[j] < root: return False if len(left_tree) > 1: #判断左子树 left = self.verifyPostorder(left_tree) elif len(left_tree) == 1: if left_tree[0] > root: return False right = True right_tree = postorder[i:len(postorder)-1] if len(right_tree) > 1: #判断右子树 right = self.verifyPostorder(right_tree) return(left & right)
思路二:递归+分治
跟上面思路一差不多,都是判断左子树所有节点均小于根节点,右子树左右节点均大于根节点,然后返回了3个bool值,当这三个均为True,这棵树才是二叉搜索树
def verifyPostorder(self, postorder: [int]) -> bool: def recur(i, j): #i j为左右边界 if i >= j: return True #此时根节点数量小于等于1,无需判别 p = i #如果左子树符合的话,p则指向左子树的和右子树的分界点 while postorder[p] < postorder[j]: p += 1 m = p #此时判断p == j如果右子树都比根节点大的话,此时p则会指向根节点的位置 while postorder[p] > postorder[j]: p += 1 #左子树区间(i ,m-1),右子树区间(m, j-1) return p == j and recur(i, m - 1) and recur(m, j - 1) #递归 return recur(0, len(postorder) - 1)
- 题解二:单调递增栈,逆向遍历数组
由于二叉搜索树是left <root < right的,后续遍历则是left-right-root,为了保证单调性,可以将后续遍历倒过来,则变成root->right->left。
翻转先序遍历是root->right->left的,基于这样的性质和遍历方式,我们知道越往右越大,这样,就可以构造一个单调递增的栈,来记录遍历的元素。
用单调栈的原因是:
往右子树遍历的过程,value是越来越大的,一旦出现了value小于栈顶元素value的时候,就表示要开始进入左子树了
(如果不是,就应该继续进入右子树,否则不满足二叉搜索树的定义),但是这个左子树是从哪个节点开始的呢?
单调栈帮我们记录了这些节点,只要栈顶元素还比当前节点大,就表示还是右子树,要移除。
因为我们要找到这个左孩子节点直接连接的父节点,也就是找到这个子树的根,只要栈顶元素还大于当前节点,就要一直弹出,直到栈顶元素小于节点,或者栈为空。栈顶的上一个元素就是子树节点的根。
接下来,数组继续往前遍历,之后的左子树的每个节点,都要比子树的根要小,才能满足二叉搜索树,否则就不是二叉搜索树。
def verifyPostorder(self, postorder: [int]) -> bool: stack, root = [], float("+inf") for i in range(len(postorder) - 1, -1, -1): if postorder[i] > root: return False #左子树比root节点大,则不是二叉搜索树 while (stack and postorder[i] < stack[-1]): root = stack.pop() #root记录子树节点的根 stack.append(postorder[i]) return True
- 单调栈
单调栈,顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。
- 单调递增栈:栈中数据出栈的序列为单调递增序列
- 单调递减栈:栈中数据出栈的序列为单调递减序列
现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。
-
10入栈时,栈为空,直接入栈,栈内元素为10。
-
3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
-
7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
-
4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
-
12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
参考链接:https://blog.csdn.net/lucky52529/article/details/89155694