二叉搜索树BST
二叉搜索树BST,即BinarySearchTree,其特性是:
- 对于任意节点,如果其值为val,则左子树所有节点值小于等于val,且右子树所有节点值大于等于val
注意利用此特性,避免多余的访问
def traverseBST(node: TreeNode, target: int):
"""BST遍历框架"""
if node is None:
return
if node.val == target:
# 找到目标,做点什么
if target < node.val: # 进入左子树
traverseBST(node.left, target)
if target > root.val: # 进入右子树
traverseBST(node.right, target)
- 中序遍历BST,一定得到一个递增序列
- BST子树,向左走到底,就是值最小的节点,向右走到底就是值最大的节点
插入新节点
LeetCode 701. 二叉搜索树中的插入操作
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if root is None: # 找到插入的空位
return TreeNode(val)
if val < root.val: # 进入左子树
root.left = self.insertIntoBST(root.left, val)
if val > root.val: # 进入右子树
root.right = self.insertIntoBST(root.right, val)
return root
判断BST合法性
LeetCode 98. 验证二叉搜索树
- 注意陷阱:不能只看每个节点和它的左右儿子是否满足要求,应该要求整个左子树、右子树都满足要求才行
- 如何保证左子树、右子树的所有节点都满足要求?
可以增加函数参数,维护min和max,限制每个节点的合法范围,从而检查节点是否合法
技巧:如果当前结点对下面的子节点有整体影响,可以借助函数参数传递更多信息
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
def valid(node, min, max):
if node is None:
return True
if min is not None and node.val <= min:
return False
if max is not None and node.val >= max:
return False
return valid(node.left, min, node.val) and valid(node.right, node.val, max)
return valid(root, None, None)
在BST中删除一个数
LeetCode 450. 删除二叉搜索树中的节点
总体框架是查找目标节点然后删除,但注意,删除后应该保持BST性质不变(分情况处理:无子节点、一个子节点、 两个子节点)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
def delete(node, key):
"""找到并删除节点,删除后应该保持BST性质不变"""
if node is None:
return None
if node.val == key: # 找到了,删除当前结点
if node.left is None and node.right is None: # 没有子节点
return None
if node.left is None: # 有一个子节点,让子节点接替自己的位置
return node.right
if node.right is None: # 有一个子节点,让子节点接替自己的位置
return node.left
# 剩下同时有左右子树的情况,可以用左子树中最大的节点接替自己,也可用右子树最小的节点接替自己
# 找到右子树最小的节点
minNode = node.right
while minNode.left is not None:
minNode = minNode.left
# 接替自己,然后删除右子树中原来的节点(此节点位于最左侧,一定没有子节点)
# ps. 当内部数据复杂时,直接修改指针更好
node.val = minNode.val
node.right = delete(node.right, minNode.val)
elif node.val > key: # 进入左子树
node.left = delete(node.left, key)
elif node.val < key: # 进入右子树
node.right = delete(node.right, key)
return node
return delete(root, key)