数据结构与算法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版》专栏!关注不迷路~

相关推荐
齐落山大勇4 小时前
数据结构——栈与队列
数据结构
进击的小头4 小时前
陷波器实现(针对性滤除特定频率噪声)
c语言·python·算法
LitchiCheng4 小时前
Mujoco 开源机械臂 RL 强化学习避障、绕障
人工智能·python·开源
知无不研4 小时前
冒泡排序算法
算法·冒泡排序·排序
毅炼4 小时前
hot100打卡——day17
java·数据结构·算法·leetcode·深度优先
404未精通的狗4 小时前
(数据结构)二叉树(上)
数据结构
Tisfy4 小时前
LeetCode 3010.将数组分成最小总代价的子数组 I:排序 OR 维护最小次小
算法·leetcode·题解·排序·最小次小值
A先生的AI之旅4 小时前
2026-1-30 LingBot-VA解读
人工智能·pytorch·python·深度学习·神经网络
Learn Beyond Limits4 小时前
文献阅读:A Probabilistic U-Net for Segmentation of Ambiguous Images
论文阅读·人工智能·深度学习·算法·机器学习·计算机视觉·ai
丝瓜蛋汤4 小时前
微调生成特定写作风格助手
人工智能·python