二叉搜索树验证之寻回丢失的智商
2023-12-31 20:15:10
首先回顾一下,二叉搜索树的定义:
- 它的每一个节点的左子树只包含比它小的元素
- 每一个节点的右子树只包含比它大的元素
- 它的左、右子树也分别为二叉搜索树
相信大家一看就会了,但是忘记的人很多,其实这也很正常。算法和数学需要非常频繁的练习。我们接触的二叉搜索树的题目非常少,再加上这道算法题的运行案例比较复杂,稍不注意就错了,这也是这道题的出错率如此之高的原因。
尝试一:二叉树中序遍历
def isValidBST(root):
"""
:type root: TreeNode
:rtype: bool
"""
def inorder(root):
if not root:
return []
return inorder(root.left) + [root.val] + inorder(root.right)
inorder_list = inorder(root)
for i in range(1, len(inorder_list)):
if inorder_list[i] <= inorder_list[i - 1]:
return False
return True
这个算法很简单,就是将二叉搜索树中序遍历后,看是否形成一个升序序列。这是验证二叉搜索树的最简单的方法之一。
然而,这个算法有一个问题,就是它需要先将整个二叉搜索树中序遍历一遍,然后才能判断是否是一个升序序列。这个算法的时间复杂度是O(n),其中n是二叉搜索树的结点个数。对于非常大的二叉搜索树,这个算法可能会非常慢。
尝试二:二叉搜索树的递归算法
def isValidBST(root):
"""
:type root: TreeNode
:rtype: bool
"""
def is_valid_bst(node, low, high):
if not node:
return True
if node.val <= low or node.val >= high:
return False
return is_valid_bst(node.left, low, node.val) and is_valid_bst(node.right, node.val, high)
return is_valid_bst(root, float('-inf'), float('inf'))
这个算法采用了递归的思想。对于每个结点,我们首先判断它的值是否在给定范围内。如果不在给定范围内,那么该二叉搜索树就不是有效的。否则,我们继续递归地判断它的左右子树是否都是有效的。
这个算法的时间复杂度是O(n),其中n是二叉搜索树的结点个数。这个算法比第一个算法要快,因为它不需要先将整个二叉搜索树中序遍历一遍。
常见错误
在验证二叉搜索树时,最常见的错误之一是忘记考虑结点的左子树和右子树。例如,以下算法是错误的:
def isValidBST(root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return True
if root.val <= root.left.val or root.val >= root.right.val:
return False
return True
这个算法只考虑了结点的值是否在它的左右子树的范围内,但是它没有考虑结点的左右子树是否都是有效的二叉搜索树。
另一个常见的错误是忘记考虑二叉搜索树的根结点的值。例如,以下算法是错误的:
def isValidBST(root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return True
if root.val <= root.left.val:
return False
if root.val >= root.right.val:
return False
return isValidBST(root.left) and isValidBST(root.right)
这个算法没有考虑二叉搜索树的根结点的值是否在它的左右子树的范围内。
小技巧
在验证二叉搜索树时,可以使用一些小技巧来提高效率。例如,我们可以使用一个栈来存储结点,而不是使用递归。这样,我们可以避免函数调用的开销。
我们还可以使用一个变量来存储二叉搜索树的最小值和最大值。这样,在判断结点的值是否在给定范围内时,我们可以直接使用这两个变量,而不是重新计算。
通过使用这些小技巧,我们可以将验证二叉搜索树的时间复杂度从O(n)降低到O(nlogn)。