二叉查找树
目录
二叉搜索树
定义
一棵二叉查找树(\(BST\))是一个二叉树,其中每个结点都含有一个\(Camparable\)的键(以及相关联的值)且每个结点的键都大于其左子树中的任意结点的键,而小于右子树的任意结点的键。
性质
二叉查找树具有以下性质:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。
常用结论
二叉查找树的中序遍历的结果是一个升序序列。
二叉树的常用操作
这里,我们直接给出二叉查找树的校验、插入、查找、删除操作的模板,模板分为递归实现和迭代实现两种。
二叉查找树的有效性校验
递归的思路
递归的时候,我们只需要将问题分解为子问题,即考虑如何判断每一个子节点的有效性即可。
直接根据性质,对于任意一个结点 \(root\),需要要保证 \(root\) 节点的值:
- 大于其左节点的值,还要小于其右节点的值;
- 大于左子树上的每一个节点值,同时,小于右子树上每一个节点的值。
那么,我们定义一个 \(validate(root, min_node, max_node)\) 函数,递归的时候,将当前节点的通过参数 \(min\_node\) 将其传给右子树,通过参数 \(max\_node\) 将其传给右子树,让每个节点依次与其比较,不满足条件,直接返回 \(False\)。
迭代的思路
由于二叉查找树的中序遍历得到的是一个升序数组,所以,可以通过栈模拟二叉树的中序遍历,比较每次记录出栈的元素是否是升序的即可。
二叉查找树的查找
二叉查找树的查找比较简单,直接利用BST左小右大的性质,进行二分搜索即可。
查找最大值
查找最大值,一路查找右子树即可。
查找最小值
查找最小值,一路查找左子树即可。
二叉查找树的插入
递归的思路
递归遍历一棵BST,找到一个合适的位置,将原有的空节点替换为新插入的节点即可。
递归的时候,由于需要通过返回值返回子树,所以不能判断待插入的节点是否以及在原有的BST上。
迭代的思路
递归遍历一棵BST,找到一个合适的位置,将原有的空节点替换为新插入的节点即可。
迭代过程中,可以判断待插入的节点是否在原有的BST上。
二叉查找树的删除
二叉查找树的删除,需要考虑三种情况:
- 场景1:待删除的节点是叶子节点,直接删除该节点即可;
- 场景2:待删除的节点有一个子节点,删除该节点后,将父节点的指针指向子节点;
- 场景2:待删除的节点有两个子节点,用左子树的最大节点,或者右子树的最小节点,替换待删除的节点。
代码实现
具体代码实现,如下:
递归实现
class Node(object):
def __init__(self, value, left=None, right=None):
self.left = left
self.right = right
self.value = value
def __str__(self):
return str(self.value)
class BST(object):
@classmethod
def is_valid(cls, root: Node) -> bool:
return cls.validate(root, None, None)
@classmethod
def validate(cls, root: Node, min_node: Node, max_node: Node) -> bool:
""" 判断以root为根节点的子树是否是BST
:param root: 根节点
:param min_node: 以root为根节点的子树中的最小节点
:param max_node: 以root为根节点的子树中的最大节点
:return: 结果
"""""
if not root:
return True
if min_node and root.value <= min_node.value:
return False
if max_node and root.value >= max_node.value:
return False
return cls.validate(root.left, min_node, root) and cls.validate(root.right, root, max_node)
@classmethod
def find(cls, value: int, tree: Node):
if not tree:
return None
if value < tree.value:
return cls.find(value, tree.left)
elif value > tree.value:
return cls.find(value, tree.right)
else:
return tree
@classmethod
def find_min(cls, tree: Node):
if not tree:
return None
if tree.left is None:
return tree
else:
return cls.find_min(tree.left)
@classmethod
def find_max(cls, tree: Node):
if not tree:
return None
if tree.right is None:
return tree
else:
return cls.find_max(tree.right)
@classmethod
def insert(cls, root: Node, target: int):
if not root:
return Node(target)
if root.value < target:
root.right = cls.insert(root.right, target)
if root.value > target:
root.left = cls.insert(root.left, target)
return root
@classmethod
def delete(cls, root: Node, target: int):
if not root:
return None
if root.key > target:
root.left = cls.delete(root.left, target)
elif root.key < target:
root.right = cls.delete(root.right, target)
else:
if not root.left:
return root.right
if not root.right:
return root.left
# 找到左子树的最大值,或者,找到右子树的最小值
min_node = cls.find_min(root.right)
root.key = min_node.key
# 删掉min节点
root.right = cls.delete(root.right, min_node.key)
return root
迭代实现
class Node(object):
def __init__(self, value, left=None, right=None):
self.left = left
self.right = right
self.value = value
def __str__(self):
return str(self.value)
class BST(object):
@classmethod
def is_valid(cls, root: Node) -> bool:
stack, inorder = [], float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
if root.value <= inorder:
return False
inorder = root.value
root = root.right
return True
@classmethod
def find(cls, value: int, tree: Node):
""" 非递归查找指定的节点 """
if not tree:
return None
while True:
if value < tree.value:
tree = tree.left
elif value > tree.left:
tree = tree.right
else:
break
return tree
@classmethod
def find_min(cls, tree: Node):
""" 非递归查找最小值的节点 """
if not tree:
return None
while tree.left:
tree = tree.left
return tree
@classmethod
def find_max(cls, tree: Node):
""" 非递归查找最大值的节点 """
if not tree:
return None
while tree.right:
tree = tree.right
return tree
@classmethod
def insert(cls, root: Node, target: int) -> bool:
if not root:
return False
# 先找到一个合适的位置
parent = None
while root:
if root.value == target:
return False
else:
parent = root
if root.value > target:
root = root.left
else:
root = root.right
# 插入到子节点
if parent.value < target:
parent.right = Node(target)
else:
parent.left = Node(target)
return True
@classmethod
def delete(cls, root: Node, key: int):
# 找到待删除的节点,及其父节点
deleted_node, delete_node_parent = root, None
while deleted_node and deleted_node.value != key:
delete_node_parent = deleted_node
if deleted_node.value > key:
deleted_node = deleted_node.left
else:
deleted_node = deleted_node.right
# 待删除的节点为空直接返回
if deleted_node is None:
return root
# 叶子节点直接删除
if deleted_node.left is None and deleted_node.right is None:
deleted_node = None
# 如果只有一个子节点,就用子节点覆盖待删除的节点
elif deleted_node.right is None:
deleted_node = deleted_node.left
elif deleted_node.left is None:
deleted_node = deleted_node.right
# 如果有两个子节点
else:
# 找到待删除节点的右子树的最小值
successor, successor_parent = deleted_node.right, deleted_node
while successor.left:
successor_parent = successor
successor = successor.left
if successor_parent.value == deleted_node.value:
successor_parent.right = successor.right
else:
successor_parent.left = successor.right
successor.right = deleted_node.right
successor.left = deleted_node.left
deleted_node = successor
if delete_node_parent is None:
return deleted_node
if delete_node_parent.left and delete_node_parent.left.value == key:
delete_node_parent.left = deleted_node
else:
delete_node_parent.right = deleted_node
return root