输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
参考以下这颗二叉搜索树:
5
/ \
2 6
/ \
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true
- 我的思路:二叉搜索树就是左节点都小于根节点,右节点都大于根节点,子树同样满足该条件。那么不难想到,递归部分就是不断分出一棵棵子树判断是否符合左小右大的特点。入参当然就是代表一棵子树的数组的起止下标。由于是后序遍历,所以右边界就是根节点。如果有边界小于左边那说明子树都判断完了,竟然在这之前都没返回 false 那只能返回 true 了。然后定义一个下标从右边界开始往前找,如果有比根节点小的,说明从右子树找到了左子树了,此时的下标就是左子树的右边界,遍历的过程其实相当于把右子树都找完了,继续往前遍历这个下标,等于遍历左子树,判断是否都小于根节点即可,然后继续判断划分出的左右子树
-
int[] pos; public boolean verifyPostorder(int[] postorder) { this.pos=postorder; return dfs(0,pos.length-1); } // root 也可以理解为 right,因为是后序遍历数组 public boolean dfs(int left,int root){ if(root < left)return true; // 找到第一个小于根节点的下标,这就是左子树的右边界 int firsetLess = root-1; while(firsetLess >=0 && pos[firsetLess]>pos[root]){ firsetLess--; } // 遍历左子树 for(int i = firsetLess;i>=0;i--){ if(pos[i]>pos[root])return false; } return dfs(firsetLess+1,root-1) && dfs(left,firsetLess); }
- 他人题解1:思路一样,不过代码实现更巧妙一些,他是从前往后找,先默认左子树都正确,然后判断没有直接判断右子树的每个值是否都大于根节点,而是只要大于根节点就往后遍历,让他遍历到根节点为止,如果能够到达根节点,说明的确是都大于根节点,否则自然是有问题了。用最后的下标是否等于根节点来转换了右子树正确性的问题。
-
public boolean verifyPostorder(int[] postorder) { 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++; // m,p 为右子树起点 int m = p; // 只要右子树合法 p 最终就会遍历到根节点,然后 postorder[p] == postorder[j]) 就结束循环了 while(postorder[p] > postorder[j]) p++; // 所以最终结果就是 p 是否等于 j 以及递归进行左右子树的判断 return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1); }
- 他人题解2:还有一种我似懂非懂的解法,把后序遍历倒序,你会得到根右左的数组,这时遍历该数组,你会先得到一个递增的数组,然后,只要发现有递减的数了,说明到了左子树,那么左子树应该满足所有数都小于根节点。根节点怎么找呢,你会发现,根节点有个特性,根节点一定是大于左节点且最接近左节点的数,比如后序数组为 132,倒序后为 231,之前的 23 都好好的,是升序的,遍历到 1 时说明到左子树了,根节点肯定在前面,所以需要用一个数据结构来存储前面的 23,2 是根,需要使用到它,3 不是根并且由于之后要判断的所有数只要小于根就行,所以之后也用不到它了,需要移除,那么能够满足我们需求的就是使用栈,存储时为 23,遍历时弹出 3 然后弹出 2,此时根节点为 2,之后遍历的数都应该小于 2
-
public boolean verifyPostorder(int[] postorder) { Stack<Integer> stack = new Stack<>(); // 初始根节点是没有根节点的,所以为了最开始是符合条件的 // 那么我们就用一定符合条件的最大值默认为他的根节点 // 因为我们是根据判断左子树来验证是否为搜索树 // 所以也就等同于我们默认根节点为一个节点的左节点,那这个结点自然是要一个一定大于根节点的数 int root = Integer.MAX_VALUE; for(int i = postorder.length - 1; i >= 0; i--) { // 若当前数大于根节点就有问题了 if(postorder[i] > root) return false; // 只要突然递减了就会进入循环寻找根节点 root // 找到找完或者存储的数小于当前节点为止 // 这也就代表我们找到了最接近当前节点的值并且大于这个值的节点 // 也就是根节点 // 也是由于整体趋势是根右左,大体呈现为降序 // 所以我们顺序遍历的整体趋势也同样是从大往小遍历 // 所以在验证的过程中 root 也是越来越小的 while(!stack.isEmpty() && stack.peek() > postorder[i]) root = stack.pop(); // 把点都 push 进去,以备之后寻找 root stack.add(postorder[i]); } return true; }
- 建议还是看原文的讲解,我唯一不理解的点就在于左节点永远不是和它的直接根节点比较,而是和爷节点比较,这是怎么验证正确性的