• 二叉树:有了如此高效的散列表,为什么还需要二叉树?


    文章来源于极客时间前google工程师−王争专栏。

    上一节我们学习了树、二叉树以及二叉树的遍历,今天我们再来学习一种特殊的的二叉树,二叉查找树。二叉查找树最大的特点就是,支持动态数据集合的快速插入、删除、查找操作。

    我们之前说过,散列表也是支持这些操作的,并且散列表的这些操作比二叉查找树更高效,时间复杂度是 O(1)。既然有了这么高效的散列表,使用二叉树的地方是不是都可以替换成成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?

    二叉查找树(Binary Search Tree)

    二叉查找树是二叉树中最常用的一种类型,也叫二叉搜索树。顾名思义,二叉查找树是为了实现快速查找而生的。不过,它不仅仅支持快速插入、删除一个数据。它是怎么做到这些的呢?

    这些都依赖于二叉查找树的特殊结构。**二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。**二叉查找树结构如下图所示:
    image

    我们来看二叉查找树是如何实现快速查找、插入、删除操作。

    1.二叉查找树的查找操作

    首先,我们看如何在二叉查找树中查找一个节点。我们先取根节点,如果它等于我们要查找的数据,那就返回。如果要查找的数据比根节点的值小,那就在左子树中递归查找;如果要查找的数据比根节点的值大,那就在右子树中递归查找。
    image

    代码实现如下:

    public class BinarySearchTree {
      private Node tree;
    
      public Node find(int data) {
        Node p = tree;
        while (p != null) {
          if (data < p.data) p = p.left;
          else if (data > p.data) p = p.right;
          else return p;
        }
        return null;
      }
    
      public static class Node {
        private int data;
        private Node left;
        private Node right;
    
        public Node(int data) {
          this.data = data;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.二叉查找树的插入操作

    二叉查找树的插入过程有点类似查找操作。新插入的数据一般都是在叶子节点上,所以我们只需要从根节点开始,依次比较要插入的数据和节点的大小关系。

    如果要插入的数据比节点的数据大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。同理,如果要插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置。

    image

    插入代码实现如下:

    public void insert(int data) {
      if (tree == null) {
        tree = new Node(data);
        return;
      }
    
      Node p = tree;
      while (p != null) {
        if (data > p.data) {
          if (p.right == null) {
            p.right = new Node(data);
            return;
          }
          p = p.right;
        } else { // data < p.data
          if (p.left == null) {
            p.left = new Node(data);
            return;
          }
          p = p.left;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    二叉查找树的查找、插入操作都比较简单易懂,但是它的删除操作就比较复杂了 。针对要删除节点的子节点个数的不同,我们需要分三种情况来处理。

    第一种情况是,如果要删除的节点没有子节点,我们只需要直接将父节点中,指向要删除节点的指针置为 null。比如图中的删除节点 55。

    第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),我们只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。比如图中的删除节点 13。

    第三种情况是,如果要删除的节点有两个子节点,这就比较复杂了。我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节(如果有左子结点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。比如图中的删除节点 18。
    image

  • 相关阅读:
    【智能家居项目】FreeRTOS版本——多任务系统中使用DHT11 | 获取SNTP服务器时间 | 重新设计功能框架
    python面经(滴滴、理想、momenta)
    洛谷 P1948 / loj 10074 / 一本通 1496【分层图】
    083-OCP题库日记
    [TQLCTF 2022]simple_bypass
    小学生python游戏编程arcade----敌人自动面向角色并开火
    使用大型语言模型进行实体提取
    C++加持让python程序插上翅膀——利用pybind11进行c++和python联合编程示例
    MySQL导致索引失效的情况详解
    Linux红帽(RHCE)认证学习笔记 - (2)用户组和用户的管理
  • 原文地址:https://blog.csdn.net/wozaibohaibian/article/details/134096618