• 24.讲二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树



    问题:既然有了这么高效的散列表,使用二叉树的地方是不是都可以替换成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?

    1. 二叉查找树(Binary Search Tree)

    又名:二叉搜索树,特点:左子树都小于节点值,右子树都大于节点值。

    1.1 查找

    从根节点开始,等于直接返回,比它小查左子树,比它大查右子树。以此类推。
    在这里插入图片描述

    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

    1.2 插入

    在这里插入图片描述

    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

    1.3 删除

    三种情况:
    在这里插入图片描述

    public void delete(int data) {
      Node p = tree; // p指向要删除的节点,初始化指向根节点
      Node pp = null; // pp记录的是p的父节点
      while (p != null && p.data != data) {
        pp = p;
        if (data > p.data) p = p.right;
        else p = p.left;
      }
      if (p == null) return; // 没有找到
    
      // 要删除的节点有两个子节点
      if (p.left != null && p.right != null) { // 查找右子树中最小节点
        Node minP = p.right;
        Node minPP = p; // minPP表示minP的父节点
        while (minP.left != null) {
          minPP = minP;
          minP = minP.left;
        }
        p.data = minP.data; // 将minP的数据替换到p中
        p = minP; // 下面就变成了删除minP了
        pp = minPP;
      }
    
      // 删除节点是叶子节点或者仅有一个子节点
      Node child; // p的子节点
      if (p.left != null) child = p.left;
      else if (p.right != null) child = p.right;
      else child = null;
    
      if (pp == null) tree = child; // 删除的是根节点
      else if (pp.left == p) pp.left = child;
      else pp.right = child;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    1.4 二叉查找树的其他操作

    快速地查找最大节点和最小节点、前驱节点和后继节点.

    重要特性:中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度是O(n),非常高效。

    2.支持重复数据的二叉查找树

    两种处理方法:

    1. 二叉查找树中每一个节点不仅会存储一个数据,因此我们通过链表和支持动态扩容的数组等数据结构,把值相同的数据都存储在同一个节点上。
    2. 每个节点仍然只存储一个数据,如果碰到一个节点的值,与要插入数据的值相同,我们就将这个要插入的数据放到这个节点的右子树。
      在这里插入图片描述

    3. 二叉查找树的时间复杂度分析

    • 平衡二叉查找树的高度接近logn,所以插入、删除、查找操作的时间复杂度也比较稳定,是O(logn)。
    • 如果退化成链表:O(n).

    4. 解答开篇

    • 散列表中的数据是无序存储的,对于二叉查找树,只需中序遍历,就可以在O(n)的时间复杂度内,输出有序的数据序列;
    • 散列表扩容耗时很多,而且当遇到散列冲突时,性能不稳定;最常用的平衡二叉查找树的性能非常稳定,时间复杂度稳定在O(logn);
    • 尽管散列表的查找等操作的时间复杂度是常量级的,但因为哈希冲突的存在,这个常量不一定比logn小,所以实际的查找速度可能不一定比O(logn)快。加上哈希函数的耗时,也不一定就比平衡二叉查找树的效率高。
    • 为了避免过多的散列冲突,散列表装载因子不能太大,特别是基于开放寻址法解决冲突的散列表,不然会浪费一定的存储空间。
  • 相关阅读:
    匿名内部类的概念和使用详解
    基于springboot的鲜花管理系统
    基于机器学习的居民消费影响因子分析预测
    ROS参数名称设置
    接口测试和功能测试有什么区别
    数据库-DQL
    Java 数组、日期和时间
    pdd.order.list.get拼多多店铺订单列表查询接口(拼多多店铺订单详情接口,订单明文接口,订单解密接口,订单插旗接口,订单备注接口)代码对接教程
    算法笔记:堆
    web网页设计期末课程大作业:美食餐饮文化主题网站设计——中华美德6页面HTML+CSS+JavaScript
  • 原文地址:https://blog.csdn.net/qq_39530821/article/details/125492028