「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」
前言
力扣第九十八题 验证二叉搜索树
如下所示:
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入: root = [2,1,3]
输出: true
复制代码
示例 2:
输入: root = [5,1,4,null,null,3,6]
输出: false
解释: 根节点的值是 5 ,但是右子节点的值是 4 。
复制代码
一、思路
二叉搜索树必须满足如下的三个条件:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
这一题我刚开始看到就写了一个简单的递归,仅判断当前节点的值是否大于左孩子的值,以及当前节点的值是否小于右孩子的值。很显然,这样做会失败,因为会碰到如下的情况:
虽然 3 < 6
且 7 > 6
,但是因为 [6, 3, 7]
作为根节点 5
的子树,他的每个节点的值都应该大于 5
,故这个树不能构成二叉搜索树。
于是我就想该怎么解决这个问题呢?
刚开始我是保存了当前节点的 父节点
的值,确保以当前节点开始的子树中的值都会 大于或小于父节点的值
,直到我又碰到了下面这种情况:
最下面的叶子节点
3
虽然大于它的父节点2
以及父节点的3
。但因为它在的子树是作为根节点3
的左孩子,所以子树中的每个节点值都应小于3
。所以这也构不成二叉搜索树。
于是我发现当前节点是否合规与他的路径有关,总结如下:
- 左节点
left
的值需要小于父节点,且大于第一次向右的值 - 右节点
right
的值需要大于父节点,且小于第一次向左的值
这是为什么呢?
其实很简单,因为 符号具有传递性,即a > b, b > c
可以得出a > c
举个例子:对于下图中的i
来说。因为g > f, f > c
,只需要i > g
就可以满足向右的节点值会一直增大。
又因为以c
节点开始的子树是b
节点的左孩子,c
节点开始的子树中的值都要小于b
节点的值。那为什么不是小于a
节点的值呢?因为即使i < a 且 b < a
,也无法判断i
与b
的大小。
所以,对于右节点来说,只需要大于父节点,且小于第一次向左的值
思路有了之后,实现起来就不难了。
二、实现
实现代码
实现代码与思路中保持一致
private class Path {
Integer val;
Boolean isLeft;
public Path(Integer val, Boolean isLeft) {
this.val = val;
this.isLeft = isLeft;
}
}
public boolean isValidBST(TreeNode root) {
return dfs(root, new LinkedList<>());
}
public boolean dfs(TreeNode root, Deque<Path> rootValStack) {
if (root == null)
return true;
// left的值需要小于父节点,且大于第一次向右的值
if (root.left != null && (root.val <= root.left.val || rootValStack.stream().anyMatch(n -> !n.isLeft && n.val >= root.left.val))){
return false;
}
// right的值需要大于父节点,且小于第一次向左
if (root.right != null && (root.right.val <= root.val || rootValStack.stream().anyMatch(n -> n.isLeft && n.val <= root.right.val))) {
return false;
}
boolean leftFlag, rightFlag;
rootValStack.push(new Path(root.val, true));
leftFlag = dfs(root.left, rootValStack);
rootValStack.pop();
rootValStack.push(new Path(root.val, false));
rightFlag = dfs(root.right, rootValStack);
rootValStack.pop();
return leftFlag && rightFlag;
}
复制代码
测试代码
public static void main(String[] args) {
TreeNode treeNode = new TreeNode(3, new TreeNode(1, new TreeNode(0), new TreeNode(2)),
new TreeNode(5, new TreeNode(4), new TreeNode(6)));
TreeNode treeNode1 = new TreeNode(2, new TreeNode(2), new TreeNode(2));
TreeNode treeNode2 = new TreeNode(120,
new TreeNode(70,
new TreeNode(50, new TreeNode(25), new TreeNode(55)),
new TreeNode(100, new TreeNode(75), new TreeNode(110))),
new TreeNode(140,
new TreeNode(130, new TreeNode(119), new TreeNode(55)),
new TreeNode(100, new TreeNode(75), new TreeNode(110)))
);
TreeNode treeNode3 = new TreeNode(3,
new TreeNode(1, new TreeNode(0), new TreeNode(2, null, new TreeNode(3))),
new TreeNode(5, new TreeNode(4), new TreeNode(6)));
boolean flag = new Number98().isValidBST(treeNode3);
System.out.println(flag);
}
复制代码
结果
三、总结
这一题击败率不是很理想,主要是因为对于每一个节点都要找到与父节点首个拐点的值是否满足条件。不知道你看了题解之后,有想到如何优化这个算法了吗?
提示一下:去除保存父节点的路径
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~