数据结构与算法Python版 二叉查找树

文章目录


一、二叉查找树

二叉查找树Binary Search Tree

  • 性质 :比父节点小的key都出现在左子树 ,比父节点大的key都出现在右子树
  • 应用:用二叉查找树保存key-value,实现key的快速搜索。把它作为抽象数据类型-映射 Map的实现方案之一。

二叉查找树的实现

  • TreeNode类:定义二叉查找树中使用的节点,包括key和val等属性
  • BinarySearchTree类:实例化TreeNode类节点对象,构建二叉查找树。并利用二叉查找树特性,实现抽象数据类型-映射。
    • _put辅助方法:如果key比currentNode小,那么_put到左子树;但如果没有左子树,那么key就成为左子节点。反之,_put到右子树或成为右子节点(当没有右子树)。
    • _get辅助方法:如果currentNode为空,查找结束;如果查找的key等于currentNode的key,返回结果;如果查找的key小于currentNode的key,那么到左子树中查找;反之,到右子树中查找。
    • _delete辅助方法:从BST中删除一个节点,还要求仍然保持BST的性质,分以下情形:
      • 这个节点没有子节点:直接删除
      • 这个节点只有左子节点:将这个子节点上移,替换被删节点。然后考虑被删节点本身是其父节点的左?还是右子节点?被删节点本身就是根节点?
      • 这个节点只有右子节点:将这个子节点上移,替换被删节点。然后考虑被删节点本身是其父节点的左?还是右子节点?被删节点本身就是根节点?
      • 这个节点有2个子节点:在被删节点的右子树中,找最小的那个节点,来替换被删节点,这个节点称为"后继"。然后调整后继节点的位置。
python 复制代码
class TreeNode:
    def __init__(self, key, val, l_child=None, r_child=None, parent=None):
        self.key = key
        self.val = val
        self.l_child: TreeNode = l_child
        self.r_child: TreeNode = r_child
        self.parent: TreeNode = parent

    def __iter__(self):
        """实现for迭代:中序遍历的迭代"""
        if self:
            if self.has_l_child():
                for elem in self.l_child:
                    yield elem
            yield self.key
            if self.has_r_child():
                for elem in self.r_child:
                    yield elem

    def has_l_child(self):
        # return True if self.l_child else False
        return False if self.l_child == None else True

    def has_r_child(self):
        return True if self.r_child else False

    def is_l_child(self):
        return self.parent and self.parent.l_child == self

    def is_r_child(self):
        return self.parent and self.parent.r_child == self

    def is_root(self):
        return not self.parent

    def is_leaf(self):
        return not (self.l_child or self.r_child)

    def has_any_children(self):
        return self.l_child or self.r_child

    def has_both_children(self):
        return self.l_child and self.r_child

    def replace_node_data(self, key, val, lc, rc):
        self.key = key
        self.val = val
        self.l_child = lc
        self.r_child = rc

    def find_successor(self):
        """寻找后继节点"""
        succ = None
        if self.has_r_child():
            succ = self.r_child.find_min()
        return succ

    def find_min(self):
        """寻找当前节点下最小节点:一直向左子节点寻找"""
        current = self
        while current.has_l_child():
            current = current.l_child
        return current

    def splice_out(self):
        """将后继节点移出"""
        if self.is_leaf():  # 如果当前是叶子节点
            if self.is_l_child():
                self.parent.l_child = None
            else:
                self.parent.r_child = None
        elif self.has_r_child:  # 如果当前节点有右子节点
            if self.is_l_child():
                self.r_child.parent = self.parent
                self.parent.l_child = self.r_child
            else:
                self.r_child.parent = self.parent
                self.parent.r_child = self.r_child

        # 后继节点不会有左子节点。因为后继节点,是被删节点的右子树中最小的。


class BinarySearchTree:
    def __init__(self):
        self.root: TreeNode = None
        self.size = 0

    def __len__(self):
        """支持调用len()获得数据项的数量"""
        return self.size

    def __iter__(self):
        """实现for迭代"""
        return self.root.__iter__()

    def __setitem__(self, key, val):
        """支持[]形式赋值"""
        self.put(key, val)

    def __getitem__(self, key):
        """支持[]索引获取值"""
        return self.get(key)

    def __contains__(self, key):
        """支持归属判断运算符in"""
        return True if self._get(key, self.root) else False

    def __delitem__(self, key):
        """实现del语法删除"""
        return self.delete(key)

    def put(self, key, val):
        """插入key val,构造BST"""
        # 如果树不为空时,调用_put()插入到BST;否则成为BST的根
        if self.root:
            self._put(key, val, self.root)
        else:
            self.root = TreeNode(key, val)

        self.size += 1

    def _put(self, key, val, cur_node: TreeNode):
        # 如果key比currentNode小,那么_put到左子树;否则,_put到左子树。
        # print(f"-----{cur_node.key,cur_node.val}--------")
        if key < cur_node.key:
            if cur_node.has_l_child():
                self._put(key, val, cur_node.l_child)
            else:
                cur_node.l_child = TreeNode(key, val, parent=cur_node)
        else:
            if cur_node.has_r_child():
                self._put(key, val, cur_node.r_child)
            else:
                cur_node.r_child = TreeNode(key, val, parent=cur_node)

    def get(self, key):
        if self.root:
            res_node = self._get(key, self.root)
            return res_node.val if res_node else None
        else:
            return None

    def _get(self, key, cur_node: TreeNode) -> TreeNode:
        if cur_node == None:
            return
        # 如果查找的key等于当前节点的key,返回结果;
        # 如果查找的key小于当前节点的key,在左孩子节点查找;否则,在右孩子查找
        if key == cur_node.key:
            return cur_node
        elif key < cur_node.key:
            return self._get(key, cur_node.l_child)
        else:
            return self._get(key, cur_node.r_child)

    def delete(self, key):
        if self.root == None:
            return False
        if self.size == 1:
            if self.root.key == key:
                self.root = None
                self.size = 0
                return True
            else:
                return False

        # 找节点,如果找不到返回False,否则删除节点
        res_node = self._get(key, self.root)
        if res_node == None:
            return False
        else:
            self._delete(res_node)
            self.size -= 1

    def _delete(self, cur_node: TreeNode):
        # 如果当前节点是叶子节点
        if cur_node.is_leaf():
            if cur_node.is_l_child():
                cur_node.parent.l_child = None
            else:
                cur_node.parent.r_child = None
            return

        # 如果当前节点只有左子节点
        if cur_node.has_l_child() and not cur_node.has_r_child():
            if cur_node.is_l_child():  # 如果当前节点是父节点的左节点
                cur_node.l_child.parent = cur_node.parent
                cur_node.parent.l_child = cur_node.l_child
            elif cur_node.is_r_child():  # 如果当前节点是父节点的右节点
                cur_node.l_child.parent = cur_node.parent
                cur_node.parent.r_child = cur_node.l_child
            else:  # 如果当前节点是根节点
                cur_node.l_child.parent = None
                self.root = cur_node.l_child
            return

        # 如果当前节点只有右子节点
        if cur_node.has_r_child() and not cur_node.has_l_child():
            if cur_node.is_l_child():
                cur_node.r_child.parent = cur_node.parent
                cur_node.parent.l_child = cur_node.r_child
            elif cur_node.is_r_child():
                cur_node.r_child.parent = cur_node.parent
                cur_node.parent.r_child = cur_node.r_child
            else:
                cur_node.r_child.parent = None
                self.root = cur_node.r_child
            return

        # 如果当前节点有左右两个子节点
        if cur_node.has_both_children():
            succ = cur_node.find_successor()
            succ.splice_out()
            cur_node.key = succ.key
            cur_node.val = succ.val

二、二叉查找树测试

  • 依次将下面6个节点插入到二叉查找树,查看二叉查当前数据项数量
  • 在二叉查找树上查找key
  • 在二叉查找树上删除key,再进行查找
python 复制代码
bst = BinarySearchTree()
bst[54] = "cat"
bst[26] = "dog"
bst[93] = "lion"
bst[17] = "tiger"
bst[77] = "bird"
bst[31] = "cow"
print(len(bst))
print(bst[20])
print(bst[26])
del bst[26]
print(bst[26])
print(bst[93])
print(len(bst))

### 输出结果
6
None
dog 
None
lion
5

三、二叉查找树算法分析

  • 查找性能决定因素在于二叉搜索树的高度(最大层次),
  • 如果key的列表是随机分布的话,那么大于和小于根节点key的键值大致相等。此时,n个节点的树高度是log2n,这样的树称为平衡树
  • 受key列表分布和插入顺序影响,put方法最优性能为O(log2n),最差性能为O(n)

您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

相关推荐
蜡笔小新星12 分钟前
Flask项目框架
开发语言·前端·经验分享·后端·python·学习·flask
cliff,14 分钟前
【python爬虫】酷狗音乐爬取
笔记·爬虫·python·学习
IT猿手14 分钟前
2025最新群智能优化算法:海市蜃楼搜索优化(Mirage Search Optimization, MSO)算法求解23个经典函数测试集,MATLAB
开发语言·人工智能·算法·机器学习·matlab·机器人
IT猿手2 小时前
2025最新群智能优化算法:山羊优化算法(Goat Optimization Algorithm, GOA)求解23个经典函数测试集,MATLAB
人工智能·python·算法·数学建模·matlab·智能优化算法
萧鼎3 小时前
深入解析 Umi-OCR:高效的免费开源 OCR 文字识别工具
python·ocr·umi-ocr
Dream it possible!5 小时前
LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)
c++·算法·leetcode
修己xj6 小时前
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
算法
梦丶晓羽7 小时前
自然语言处理:文本分类
人工智能·python·自然语言处理·文本分类·朴素贝叶斯·逻辑斯谛回归
开心比对错重要7 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode
美狐美颜sdk7 小时前
什么是美颜SDK?从几何变换到深度学习驱动的美颜算法详解
人工智能·深度学习·算法·美颜sdk·第三方美颜sdk·视频美颜sdk·美颜api