二叉查找树

二叉搜索树

定义

一棵二叉查找树\(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
posted @ 2022-12-07 23:25  LARRY1024  阅读(46)  评论(0编辑  收藏  举报