二叉搜索树 BST

381 阅读3分钟

⒈ 树型数据结构

   树是分层的数据结构,最顶层的节点成为根节点。每个节点的下层节点成为该节点的子节点,子节点所附属的上层节点成为该节点的父节点。没有子节点的节点成为树的叶子节点。

   一棵树所能容纳的节点数量理论上没有上限。

⒉ 二叉树的基本属性

   二叉树是值每个父节点最多包含两个子节点的树

设二叉树的深度为 D,节点总数为 N,叶子节点的总数为 L,则二叉树有如下属性:

  • 深度为 D 的层所包含的节点数量的上限为 2D2^D
  • 节点总数 N 的上限为 2D+112^{D + 1} - 1
  • 深度 D 的最小值为 log2(N+1)1log_2(N + 1) - 1
  • 深度 D 的最小值为 log2Llog_2L
  • 如果一颗二叉树中所有节点都有 0 个或 2 个子节点,则二叉树叶子节点的数量比非叶子节点的数量多 1

二叉树的深度高度宽度

  • 深度:某个节点到根节点的路径所经历的边的数量,根节点深度为 0
  • 高度:某个节点到叶子节点的最长路径所经历的边的数量,叶子节点的高度为 0
  • 宽度:任何两个叶子节点之间的最长路径所包含的节点的数量

二叉树深度、高度示意图

⒊ 二叉搜索树 BST

  ⓵ BST 的节本特征

  • 左子树的任何节点的值都小于根节点的值
  • 右子树的任何节点的值都大于根节点的值
  • 任一子树都是 BST

  ⓶ BST 节点插入、搜索、删除 API 实现

     定义二叉树节点的数据结构,每个节点包含节点自身存储的值以及节点左右子树。

class Node:
    def __init__(self, item):
        self.item = item
        self.left = None
        self.right = None

	# 以下两个方法用于在 print 树结构时完整输出整个树结构的各个节点,而不是仅输出一个内存地址
    def __repr__(self):
        return str(self.__dict__)

    def __str__(self):
        return str(self.__dict__)
    ⅰ 节点插入

     节点插入的实现逻辑:

       如果二叉树没有任何节点,则将插入节点作为根节点 ;

       如果插入节点的值小于根节点的值,则将节点插入左子树;

       如果插入节点的值小于根节点的值,则将节点插入右子树。

class BST:
	def insert(self, root, key):
        """
        向 BST 插入节点
        :param root:
        :param key:
        :return:
        """
        if root is None:
            root = Node(key)
        elif key < root.item:
            root.left = self.insert(root.left, key)
        else:
            root.right = self.insert(root.right, key)

        return root

注意:以上方法插入节点到二叉树,如果节点的插入顺序正好是升序或降序,则最终会生成一棵倾斜的树。

如何在往二叉树插入节点的过程中保持树的平衡,以后在将自平衡二叉树(AVL)的时候会提到


    ⅱ 节点搜索

     节点搜索的实现逻辑与插入逻辑类似:

      如果树为空,则直接返回;

      如果节点的值正好等于根节点的值,则返回根节点;

      如果节点的值小于根节点的值,则在左子树中继续搜索,直到找到一个值与搜索节点的值相等的节点或找不到与搜索节点的值相等的点

      如果节点的值大于根节点的值,则在右子树中继续搜索。

	def search(self, root, key):
        """
        BST 搜索
        :param root:
        :param key:
        :return:
        """
        if root is None:
            return None

        if key == root.item:
            return root
        elif key < root.item:
            return self.search(root.left, key)
        else:
            return self.search(root.right, key)
    ⅲ 节点删除

     节点删除的逻辑会比较复杂:

       如果要删除的节点是叶子节点,则直接删除;

       如果要删除的节点有一个子节点,则用子节点替换要删除的节点;

       如果要删除的节点包含两个子节点,则在该节点的右子树中搜索值最小的节点 M。如果 M 为要删除节点的子节点,则直接用 M 节点替换要删除的节点;如果 M 不是要删除的节点的子节点,则先将 M 节点的右子树作为 M 父节点的左子树,然后用 M 节点替换要删除的节点。

	def delete(self, root, key):
        """
        从 BST 删除节点
        :param root:
        :param key:
        :return:
        """
        if root is None:
            return root

        if key < root.item:
            root.left = self.delete(root.left, key)
        elif key > root.item:
            root.right = self.delete(root.right, key)
        else:
            # key == root.item
            if root.left is None:
                temp = root.right
                root = temp
            elif root.right is None:
                temp = root.left
                root = temp
            else:
                # 要删除的节点拥有两个子节点的情况
                successor_parent = root
                successor = root.right

                while successor.left is not None:
                    successor_parent = successor
                    successor = successor.left

                if successor_parent != root:
                    successor_parent.left = successor.right
                else:
                    successor_parent.right = successor.right

                root.item = successor.item

        return root

以下为测试代码

bst = BST()
root = bst.insert(None, 50)
root = bst.insert(root, 30)
root = bst.insert(root, 20)
root = bst.insert(root, 40)
root = bst.insert(root, 70)
root = bst.insert(root, 60)
root = bst.insert(root, 80)

print(root)
node = bst.search(root, 70)
print(node)

root = bst.delete(root, 50)
print(root)