python-二叉搜索树(BST)

 二叉搜索树(BST)

(1)别名:二叉查找树、二叉搜索树。

(2)根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一个节点的值。

(3)BST的中序序列一定是升序的

(4)二叉搜索树的操作:查询、插入、删除

二叉搜索树的插入

首先检查树是否有根。如果没有根,那么 insert 将创建一个新的 TreeNode 并将其做为树的根。如果根节点已经就位,则 insert 调用私有递归辅助函数 insert根据以下流程搜索树:

        (1)从树的根开始,搜索二叉树,将新键与当前节点中的键进行比较。如果新键小于当前节点,则搜索左子树。如果新键大于当前节点,则搜索右子树。

        (2)当没有左(或右)孩子要搜索时,我们在树中找到应该建立新节点的位置。

        (3)向树中添加节点时,先创建一个新的 BiTreeNode对象,并将对象插入到上一步发现的节点。插入时,总是作为叶节点插入到树中。

给定序列去依次构造二叉查找树,则二叉搜索树唯一;如果是用这个序列所有的关键字去构造可能的二叉树(排列任意),则一般不唯一。

二叉搜索树的查询

当二叉查找树不为空时:

        (1)首先将给定值与根结点的关键字比较,若相等,则查找成功

        (2)若小于根结点的关键字值,递归查左子树

        (3)若大于根结点的关键字值,递归查右子树

        (4)若子树为空,查找不成功

二叉搜索树的删除

第一个任务是通过搜索树来找到要删除的节点。 如果树具有多个节点,我们使用 query方法搜索以找到需要删除的 BiTreeNode。 如果树只有一个节点,这意味着我们删除树的根,但是我们仍然必须检查以确保根的键匹配要删除的键。 在任一情况下,如果未找到键,del 操作符将引发错误。

一旦我们找到了我们要删除的键的节点,我们必须考虑三种情况:
        (1)要删除的节点没有子节点。

        (2)要删除的节点只有一个子节点。

        (3)要删除的节点有两个子节点。

第一种情况:

如果当前节点没有子节点,我们需要做的是删除节点并删除对父节点中该节点的引用。

第二种情况:

如果一个节点只有一个孩子,那么我们可以简单地将孩子取代其父。一般我们有六种情况要考虑。由于这些情况相对于左孩子或右孩子对称,我们将仅讨论当前节点具有左孩子的情况。决策如下:

如果当前节点是左子节点,则我们只需要更新左子节点的父引用以指向当前节点的父节点,然后更新父节点的左子节点引用以指向当前节点的左子节点。

如果当前节点是右子节点,则我们只需要更新左子节点的父引用以指向当前节点的父节点,然后更新父节点的右子节点引用以指向当前节点的左子节点。

如果当前节点没有父级,则它是根。在这种情况下,我们将通过在根上调用replaceNodeData 方法来替换 key,payload,leftChild 和 rightChild 数据。

第三种情况:

最难处理的情况。 如果一个节点有两个孩子,那么我们不太可能简单地提升其中一个节点来占据节点的位置。 然而,我们可以在树中搜索可用于替换被调度删除的节点的节点。 我们需要的是一个节点,它将保留现有的左和右子树的二叉搜索树关系。 执行此操作的节点是树中具有次最大键的节点。 我们将这个节点称为后继节点,我们将看一种方法来很快找到后继节点。 继承节点保证没有多于一个孩子,所以我们知道使用已经实现的两种情况删除它。 一旦删除了后继,我们只需将它放在树中,代替要删除的节点。找到后继的代码如下所示,是BiTreeNode 类的一个方法。

一般我们利用BST的特性:中序遍历是升序。因此采用中序遍历从最小到最大打印树中的节点。在寻找接班人时,有三种情况需要考虑:

如果节点有右子节点,则后继节点是右子树中的最小的键。

如果节点没有右子节点并且是父节点的左子节点,则父节点是后继节点。

如果节点是其父节点的右子节点,并且它本身没有右子节点,则此节点的后继节点是其父节点的后继节点,不包括此节点。

参考:https://www.php.cn/python-tutorials-412354.html

总结:删除操作——先中序遍历,然后把删除节点的前继和后继提上去

"""
======================
-*- coding: utf-8 -*-
@author: Qiufen.Chen
@time: 2021/7/24:22:28
@email: 1760812842@qq.com
@File: bst.py
@Project: data_structure
======================
"""

"""二叉搜索树(BST)的操作"""
# 定义二叉树
class BiTreeNode:
    def __init__(self,data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子
        self.parent = None

class BST:
    def __init__(self, li=None):
        self.root = None
        if li:
            for val in li:
                self.insert_no_cur(val)

    # 递归插入
    def insert(self, node, val):
        if not node:
            node = BiTreeNode(val)
        elif val < node.data:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        elif val > node.data:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    # 非递归插入
    def insert_no_cur(self, val):
        p = self.root
        if not p:
            self.root = BiTreeNode(val)
            return
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:
                    p.lchild = BiTreeNode(val)
                    p.lchild.parent = p
                    return
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:
                    p.rchild = BiTreeNode(val)
                    p.rchild.parent = p
                    return
            else:
                return

    # 递归查找
    def query(self, node, val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild, val)
        elif node.data > val:
            return self.query(node.lchild, val)
        else:
            return node

    # 非递归查找
    def query_no_rec(self, val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rchild
            elif p.data > val:
                p = p.lchild
            else:
                return p
        return None

    def _remove_node_1(self,node):
        # 情况1:node是叶子结点
        if not node.parent:
            self.root = None
        if node == node.parent.lchild: # node是它父亲的左孩子
            node.parent.lchild = None
        else:  # 右孩子
            node.parent.rchild = None

    def _remove_node_21(self, node):
        # 只有一个左孩子
        if not node.parent:
            self.root = node.lchild
            node.lchild.parent = None
        elif node == node.parent.lchild:
            node.parent.lchild = node.lchild
            node.lchild.parent = node.parent
        else:
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    def _remove_node_22(self, node):
        # 只有一个右孩子
        if not node.parent:
            self.root = node.rchild
        elif node == node.parent.lchild:
            node.parent.lchild = node.rchild
            node.rchild.parent = node.parent
        else:
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent

    def delete(self, val):
        if self.root: # 不是空树
            node = self.query_no_rec(val)  # 找不到返回None
            if not node:  # 不存在
                return False
            if not node.lchild and not node.rchild:  # 1. 叶子结点
                self._remove_node_1(node)
            elif not node.rchild:   # 2.1 只要一个左孩子
                self._remove_node_21(node)
            elif not node.lchild:  # 2.2 只有一个右孩子
                self._remove_node_22(node)
            else:
                # 3.两个孩子都有
                min_node = node.rchild
                while min_node.lchild:
                    min_node = min_node.lchild
                node.data = min_node.data
                # 删除min_node
                if min_node.lchild:
                    self._remove_node_22(min_node)
                else:
                    self._remove_node_1(min_node)

    # 二叉树的中序遍历
    def in_order(self, root):
        if root:
            self.in_order(root.lchild)
            print(root.data, end=',')
            self.in_order(root.rchild)


if __name__=="__main__":
    tree = BST([4,6,7,9,2,1,3,5,8])
    tree.in_order(tree.root)
    print("")
    tree.delete(6)
    tree.in_order(tree.root)

 二叉搜索树的效率

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值