算法之树(二,B+树、哈夫曼树、堆、红黑树)(Java版)-持续更新补充

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: B+树的优势 1.单一节点存储更多元素。B+树中间节点没有卫星数据(也就是说只包含索引信息),所以每个非叶子节点可以包含更多的内容,同样大小的磁盘页可以容纳更多的节点元素。也就是说B+树会在相同数据量的情况下比B树更加“矮胖”,查询的IO次数更少。

接着来搞树!

支持云栖社区,也希望大家能支持下我的独立博客——白水东城
文章地址:
算法之树(二,B+树、哈夫曼树、堆、红黑树)(Java版)-持续更新补充

一、B+树

B+树的特征

  1. 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点
  2. 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接
  3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素
    如图(图片来自程序员小灰)

此处输入图片的描述

在程序员小灰的公众号里提到了一个概念——卫星数据:索引元素指向的数据记录,比如数据库中的某一行。在B+树中只有叶子节点带卫星数据,其他的中间节点只是索引,没有任何数据关联。在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。 吧

B+树的优势

  1. 单一节点存储更多元素。B+树中间节点没有卫星数据(也就是说只包含索引信息),所以每个非叶子节点可以包含更多的内容,同样大小的磁盘页可以容纳更多的节点元素。也就是说B+树会在相同数据量的情况下比B树更加“矮胖”,查询的IO次数更少。
  2. 查询效率稳定。B+树的查询必须最终找到叶子节点,而B树如果在中间节点找到匹配的即可(最好情况是只查根节点,最差是查到叶子节点),而B+树每一次都是稳定的。B-树的好处是,虽然查询性能不稳定,但平均的查询速度快一些。试想一个数据库的查询,有时候执行10毫秒,有时候执行100毫秒,肯定是不太合适的。还不如每次都执行30毫秒。
  3. 范围查询简便。B树的范围查询只能依靠繁琐的中序遍历,找到下限和上限。而B+树的范围查询很简单,只需要在叶子节点那一层的链表上做遍历就行

为什么数据库中一定要索引

二分查找,二叉树查找都依赖特定的数据结构,分别是待查找数据有序、二叉查找树,显然数据本身不能完全满足各种数据结构。
所以,数据库除了维护数据之外,还维护者满足特定查找算法的数据结构——索引,索引以某种方式引用数据,这样就可以在索引的基础上实现高级的查找等操作。目前大部分数据库系统和文件系统都采用B树或者变种的B+树来作为索引结构

为什么MySQL数据库中使用B+树

1.局部性原理与磁盘预读
由于磁盘的存取速度与内存之间鸿沟,为了提高效率,要尽量减少磁盘I/O.磁盘往往不是严格按需读取,而是每次都会预读,磁盘读取完需要的数据,会顺序向后读一定长度的数据放入内存。而这样做的理论依据是计算机科学中著名的局部性原理:

当一个数据被用到时,其附近的数据也通常会马上被使用,程序运行期间所需要的数据通常比较集中

由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率.预读的长度一般为页(page)的整倍数。

2.数据库索引采用B+树的主要原因
根据上面的局部性原理和磁盘预读,B树中用了这个技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个结点只需一次I/O。
B树在提高了IO性能的同时并没有解决元素遍历的效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁(比如查询某段时间之内的数据)的,而B树不支持这样的操作或者说效率太低(前文已经说明效率低的原因)。

二、哈夫曼树

带权路径长度最小的树就叫最优二叉树,也就是哈夫曼树。要使带权路径长度最小,那么权值大的点就应该离根节点越近。
构造方法:先从小到大排序,然后选择最小的两棵树合并,重复这两个步骤。

哈夫曼编码

如果对一段英文转换为二进制传输,采用哈夫曼编码,让频率高的用短码,频率低的用长码,而且保证不会有某个字符的串是另一个字符的前缀(因为如果每个字符的长度不一样会出现这样的问题,比如,一个字符被11表示,另一个被110表示,出现一段11011,这样就有歧义)

哈夫曼树实现

之前数据结构课上用C写过哈夫曼树,Java的暂时不搞了,之后遇见再回来补充。

三、堆

堆是一个完全二叉树,大顶堆就是每个节点都不大于它的父节点。
插入和删除时间复杂度都是O(logn)。

堆排序

初始化一个堆,然后把无序数组的每个值都依次插入堆中,然后一直删除,把被删除的元素放到数组的最后一个有效元素之后的位置。

public class Heap {
    private int[] element;
    
    public Heap(int maxSize) {
        element = new int[maxSize];
        element[0] = 0;//存放堆中实际的个数
    }
    
    public boolean isEmpty() {
        return element[0] == 0;
    }
    public boolean isFull() {
        return element[0] == element.length - 1;
    }
    public void insert(int value) {
        if(isFull()) {
            throw new IndexOutOfBoundsException("堆已经满啦..");
        }
        if(isEmpty()) {
            element[1] = value;
        }else {
            int i = element[0] + 1;
            while(i != 1 && value > element[i / 2]) {
                element[i] = element[i / 2];
                i /= 2;
            }
            element[i] = value;
        }
        element[0] ++;
    }
    public int delete() {
        if(isEmpty()) {
            throw new IndexOutOfBoundsException("堆空啦");
        }
        int deleteElement = element[1];
        element[1] = element[element[0]];
        element[0]--;
        int value = element[1];
        int parent = 1;
        int child = 2;
        while(child <= element[0]) {
            if(child + 1 <= element[0] && element[child] < element[child + 1]) {
                child ++;
            }
            if(value >= element[child]) {
                break;
            }else {
                element[parent] = element[child];
                parent = child;
                child *= 2;
            }
        }
        element[parent] = value;
        return deleteElement;
    }
    public void printAll() {
        for(int i = 0; i < element[0]; i++) {
            System.out.print(element[i]);
            if(i != element[0]) {
                System.out.print(",");
            }
        }
        System.out.println();
    }
    public void sort() {
        int size = element[0];
        for(int i = 0; i < size; i++) {
            int deleteElement = delete();
            element[element[0] + 1] = deleteElement;
        }    
        for(int i = 1; i <= size; i++) {
            System.out.print(element[i]);
            if(i != size) {
                System.out.print(",");
            }
        }
    }
}

红黑树

红黑树的插入、删除、查找最坏时间复杂度都是O(logn)。
红黑树理解概念就OK了,目前不深入研究。
推荐一篇很好的对红黑树的概念理解文章:
漫画:什么是红黑树?

文章地址:
算法之树(二,B+树、哈夫曼树、堆、红黑树)(Java版)-持续更新补充
参考

  1. 《轻松学算法》赵烨
  2. 漫画:什么是B+树?
  3. 为什么MySQL数据库索引选择使用B+树?
  4. 数据库为什么要用B+树结构--MySQL索引结构的实现
  5. 由 B-/B+树看 MySQL索引结构
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
30天前
|
Java
【Java基础面试三十二】、new String(“abc“) 是去了哪里,仅仅是在堆里面吗?
这篇文章解释了Java中使用`new String("abc")`时,JVM会将字符串直接量"abc"存入常量池,并在堆内存中创建一个新的String对象,该对象会指向常量池中的字符串直接量。
|
21天前
|
存储 算法 Java
惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
101 4
|
30天前
|
搜索推荐 算法 Java
手写快排:教你用Java写出高效排序算法!
快速排序(QuickSort)是经典的排序算法之一,基于分治思想,平均时间复杂度为O(n log n),广泛应用于各种场合。在这篇文章中,我们将手写一个Java版本的快速排序,从基础实现到优化策略,并逐步解析代码背后的逻辑。
41 1
|
30天前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
47 1
|
28天前
|
存储 算法 Java
解释 Java 堆空间和垃圾收集
【8月更文挑战第22天】
25 0
|
20天前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
37 2
|
28天前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
28天前
|
存储 Java
|
22天前
|
数据采集 算法
基于PSO粒子群算法的三角形采集堆轨道优化matlab仿真
该程序利用PSO算法优化5个4*20矩阵中的模块采集轨迹,确保采集的物品数量及元素含量符合要求。在MATLAB2022a上运行,通过迭代寻优,选择最佳模块组合并优化轨道,使采集效率、路径长度及时间等综合指标最优。具体算法实现了粒子状态更新、需求量差值评估及轨迹优化等功能,最终输出最优轨迹及其相关性能指标。
|
28天前
|
存储 Java