1.中序遍历结果为升序
最直观/直接的解法:对二叉搜索树进行中序遍历,将结果存入一个数组,然后判断数组是否为生序即可验证二叉搜索树
private List<Integer> list;
public boolean isValidBST(TreeNode root) {
list = new ArrayList<>();
addNode(root);
// 判断是否为升序
for (int i = 1; i < list.size(); ++i) {
if (list.get(i-1)>= list.get(i)) return false;
}
return true;
}
// 中序遍历二叉树并添加节点至list
private void addNode(TreeNode root) {
if (root == null) return;
addNode(root.left);
list.add(root.val);
addNode(root.right);
}
时间复杂度:O(n),分别对树和list进行了一次遍历
空间复杂度:O(n),使用了额外的list来存储元素
2.中序遍历(优化)
通过观察可以发现比较大小这个操作其实可以融合到中序遍历中,省去数组存储和比较的过程。即一边遍历一边比较大小。
如上图中遍历的顺序是[C, B, E, D, A, F, G],只要在每次新遍历到一个节点时,比较当前节点.val和上一节点.val的大小关系即可判断是否为升序(二叉搜索树)。
private long min = Long.MIN_VALUE; // 使用long,防止边界值溢出问题
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (!isValidBST(root.left)) return false;
if (root.val <= min) return false;
min = root.val;
return isValidBST(root.right);
}
时间复杂度:O(n)
空间复杂度:O(1)
3.递归(分治)
前两种方法的递归主要侧重于使用中序遍历的顺序来遍历二叉树,除此之外还可以直接递归地解决这个问题,但在递归的同时,必须给子问题传入一个相关提示,即左右边界值:当递归左子树时,任何节点值都不能超过根节点值;当递归右子树时,任何节点值都不能小于根节点值。如此,才能满足二叉搜索树。
public boolean isValidBST(TreeNode root) {
return isValidBST_helper(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean isValidBST_helper(TreeNode root, long left, long right) {
if (root == null) return true;
if (root.val <= left || root.val >= right) return false;
return isValidBST_helper(root.left, left, root.val) && isValidBST_helper(root.right, root.val, right);
}
时间复杂度:O(n)
空间复杂度:O(n)