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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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(",");
            }
        }
    }
}
AI 代码解读

红黑树

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

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

  1. 《轻松学算法》赵烨
  2. 漫画:什么是B+树?
  3. 为什么MySQL数据库索引选择使用B+树?
  4. 数据库为什么要用B+树结构--MySQL索引结构的实现
  5. 由 B-/B+树看 MySQL索引结构
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
探究‘公司禁用 U 盘’背后的哈希表算法与 Java 实现
在数字化办公时代,信息安全至关重要。许多公司采取“禁用U盘”策略,利用哈希表算法高效管理外接设备的接入权限。哈希表通过哈希函数将设备标识映射到数组索引,快速判断U盘是否授权。例如,公司预先将允许的U盘标识存入哈希表,新设备接入时迅速验证,未授权则禁止传输并报警。这有效防止恶意软件和数据泄露,保障企业信息安全。 代码示例展示了如何用Java实现简单的哈希表,模拟公司U盘管控场景。哈希表不仅用于设备管理,还在文件索引、用户权限等多方面助力信息安全防线的构建,为企业数字化进程保驾护航。
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
清华大学与腾讯联合推出的Video-T1技术,通过测试时扩展(TTS)和Tree-of-Frames方法,显著提升视频生成的连贯性与文本匹配度,为影视制作、游戏开发等领域带来突破性解决方案。
54 4
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
|
30天前
|
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
42 3
 算法系列之数据结构-Huffman树
8G的容器Java堆才4G怎么就OOM了?
本文记录最近一例Java应用OOM问题的排查过程,希望可以给遇到类似问题的同学提供参考。
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
81 2
【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)
【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)
199 0
数据结构算法学习打卡week2 (Java)
数据结构算法学习打卡week2 (Java)
119 0
【算法学习】1588. 所有奇数长度子数组的和(java / c / c++ / python / go / rust)
给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。 子数组 定义为原数组中的一个连续子序列。 请你返回 arr 中 所有奇数长度子数组的和 。
【算法学习】1588. 所有奇数长度子数组的和(java / c / c++ / python / go / rust)
【算法学习】剑指 Offer II 054. 所有大于等于节点的值之和|538|1038(java / c / c++ / python / go / rust)
给定一个二叉搜索树,请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。 提醒一下,二叉搜索树满足下列约束条件: 节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左右子树也必须是二叉搜索树。
【算法学习】剑指 Offer II 054. 所有大于等于节点的值之和|538|1038(java / c / c++ / python / go / rust)
【算法学习】剑指 Offer II 083. 没有重复元素集合的全排列|46. 全排列(java / c / c++ / python / go / rust)
给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。
【算法学习】剑指 Offer II 083. 没有重复元素集合的全排列|46. 全排列(java / c / c++ / python / go / rust)