数据结构小记【Python/C++版】——B树篇

news2024/11/17 11:33:31

一,基础概念

B树也是一种自平衡搜索树,常用于数据库中索引的实现。

B树和AVL树的区别在于:

B树是一种多路平衡查找树,B树的节点可以有两个以上的子节点(AVL树是二叉树,最多只能有两个子节点)。

B树的每个节点可以存储一个以上的数据域(在之前介绍过的树结构中,一个节点只有一个数据域)。由于B树的节点可以存放多条数据,因此B树特别适合应用在块存储的开发场景。

B树和AVL树的相同点在于:

B树的节点分布也是按照左小右大排列,子节点与节点的大小比较结果决定了子节点的位置。

每个节点中,左子树的数据比当前节点都小,右子树的数据比当前节点都大。

关于B树的阶:

M阶B树表示该树每个节点最多有M个子树。若B树中一个节点的子节点数目的最大值为4,则该树为4阶。

M阶B树可以定义为M路搜索树:

节点的最多子节点数为M。

节点的最大数据量为M-1。

所有非叶子节点(根节点除外)应该至少有M/2个子节点。

所有节点(根节点除外)应该至少有M/2-1条数据。

B树的阶数可以帮助我们确定节点在B树中可以容纳的子节点数。如果B树的阶数为3,则子节点的最小数量为2,最大数量为3,节点最多可以存放2个数据。类似地,如果B树的阶数为4,则子节点的最小和最大数量将分别为2和4,节点最多可以存放3个数据。

B树的基本结构:

B树的结构特点:

所有叶子节点都出现在同一水平高度。

B树是一棵扁平树,层数很小,B树的节点不再只存储一条数据。

B树节点内的所有数据必须按键值升序排列。

B树的设计原则就是,将尽可能多的数据放入B树的每个节点中,从而使B树的层数保持在最小值。由于不用遍历很多层节点,与其他平衡树相比,B树的访问速度更快。

二,B树的基本操作

搜索节点:

节点在B树中的搜索步骤与BST树中的类似。

插入节点:

如果树为空,则创建一个新节点并将其作为根节点插入到树中。

如果树不为空,插入节点包含两个操作:

1.搜索合适的位置以插入节点

按照升序插入节点。

2.当节点的数据量过大时拆分节点

插入新节点以后,节点中数据的数量超过了限制(节点的最大数据量为M-1),则按中位数拆分。将中间节点推往上一层,使左边节点成为左子节点,右边节点成为右子节点。

图示样例:

三,完整代码实现

C++实现:

#include <iostream>
using namespace std;
class Node {
    int* keys;
    int t;
    Node** C;
    int n;
    bool leaf;
public:
    Node(int _t, bool _leaf);
    void insertNonFull(int k);
    void splitChild(int i, Node* y);
    void traverse();
    friend class BTree;
};
class BTree {
    Node* root;
    int t;
public:
    BTree(int _t) {
        root = NULL;
        t = _t;
    }
    void traverse() {
        if (root != NULL)
            root->traverse();
    }
    void insert(int k);
};
Node::Node(int t1, bool leaf1) {
    t = t1;
    leaf = leaf1;
    keys = new int[2 * t - 1];
    C = new Node * [2 * t];
    n = 0;
}
//遍历节点
void Node::traverse() {
    int i;
    for (i = 0; i < n; i++) {
        if (leaf == false)
            C[i]->traverse();
        cout << " " << keys[i];
    }
    if (leaf == false)
        C[i]->traverse();
}
void BTree::insert(int k) {
    if (root == NULL) {
        root = new Node(t, true);
        root->keys[0] = k;
        root->n = 1;
    }
    else {
        if (root->n == 2 * t - 1) {
            Node* s = new Node(t, false);
            s->C[0] = root;
            s->splitChild(0, root);
            int i = 0;
            if (s->keys[0] < k)
                i++;
            s->C[i]->insertNonFull(k);
            root = s;
        }
        else
            root->insertNonFull(k);
    }
}
void Node::insertNonFull(int k) {
    int i = n - 1;
    if (leaf == true) {
        while (i >= 0 && keys[i] > k) {
            keys[i + 1] = keys[i];
            i--;
        }
        keys[i + 1] = k;
        n = n + 1;
    }
    else {
        while (i >= 0 && keys[i] > k)
            i--;
        if (C[i + 1]->n == 2 * t - 1) {
            splitChild(i + 1, C[i + 1]);
            if (keys[i + 1] < k)
                i++;
        }
        C[i + 1]->insertNonFull(k);
    }
}
//拆分节点
void Node::splitChild(int i, Node* y) {
    Node* z = new Node(y->t, y->leaf);
    z->n = t - 1;
    for (int j = 0; j < t - 1; j++)
        z->keys[j] = y->keys[j + t];
    if (y->leaf == false) {
        for (int j = 0; j < t; j++)
            z->C[j] = y->C[j + t];
    }
    y->n = t - 1;
    for (int j = n; j >= i + 1; j--)
        C[j + 1] = C[j];
    C[i + 1] = z;
    for (int j = n - 1; j >= i; j--)
        keys[j + 1] = keys[j];
    keys[i] = y->keys[t - 1];
    n = n + 1;
}
int main() {
    BTree t(3);
    t.insert(8);
    t.insert(9);
    t.insert(10);
    t.insert(11);
    t.insert(15);
    t.insert(20);
    t.insert(17);
    cout << "The B-tree is: ";
    t.traverse();
}

运行结果:

The B-tree is:  8 9 10 11 15 17 20

Python实现:

class BTreeNode:
    def __init__(self, leaf=False):
        self.leaf = leaf
        self.keys = []
        self.child = []

class BTree:
    def __init__(self, t):
        self.root = BTreeNode(True)
        self.t = t

    def insert(self, k):
        root = self.root
        if len(root.keys) == (2 * self.t) - 1:
            temp = BTreeNode()
            self.root = temp
            temp.child.insert(0, root)
            self.split_child(temp, 0)
            self.insert_non_full(temp, k)
        else:
            self.insert_non_full(root, k)

    def insert_non_full(self, x, k):
        i = len(x.keys) - 1
        if x.leaf:
            x.keys.append((None, None))
            while i >= 0 and k[0] < x.keys[i][0]:
                x.keys[i + 1] = x.keys[i]
                i -= 1
            x.keys[i + 1] = k
        else:
            while i >= 0 and k[0] < x.keys[i][0]:
                i -= 1
            i += 1
            if len(x.child[i].keys) == (2 * self.t) - 1:
                self.split_child(x, i)
                if k[0] > x.keys[i][0]:
                    i += 1
            self.insert_non_full(x.child[i], k)

    def split_child(self, x, i):
        t = self.t
        y = x.child[i]
        z = BTreeNode(y.leaf)
        x.child.insert(i + 1, z)
        x.keys.insert(i, y.keys[t - 1])
        z.keys = y.keys[t: (2 * t) - 1]
        y.keys = y.keys[0: t - 1]
        if not y.leaf:
            z.child = y.child[t: 2 * t]
            y.child = y.child[0: t - 1]

    def print_tree(self, x, l=0):
        print("Level ", l, " ", len(x.keys), end=":")
        for i in x.keys:
            print(i, end=" ")
        print()
        l += 1
        if len(x.child) > 0:
            for i in x.child:
                self.print_tree(i, l)

def main():
    B = BTree(3)
    for i in range(10):
        B.insert((i, 2 * i))
    B.print_tree(B.root)

if __name__ == '__main__':
    main()

四,参考阅读

https://iq.opengenus.org/b-tree-searching-insertion/

https://www.programiz.com/dsa/insertion-into-a-b-tree

https://www.programiz.com/dsa/b-plus-tree

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1506874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是SSL洪水攻击,有办法处理吗?

相信不少人曾遇到过或是听说过DDOS攻击&#xff0c;分布式拒绝服务&#xff08;DDoS&#xff09;攻击指多个系统联合进行攻击&#xff0c;从而使目标机器停止提供服务或资源访问。大致来说&#xff0c;就是让多台电脑向一台服务器发送大量的流量&#xff0c;直到该服务器崩溃掉…

利用GPT开发应用007:警惕人工智能幻觉,局限与注意事项

文章目录 一、人工智能幻觉二、计算案例三、斑马案例四、总结 正如您所见&#xff0c;一个大型语言模型通过基于给定的输入提示逐个预测下一个单词&#xff08;或标记&#xff09;来生成答案。在大多数情况下&#xff0c;模型的输出对您的任务来说是相关的&#xff0c;并且完全…

解决Ubuntu 16.04/18.04 图形化界面异常、鼠标光标消失、鼠标变成叉叉等问题

bug场景&#xff1a; 一切从一次换源说起…叭叭叭 这篇文章解决的问题&#xff1a; 1.换源&#xff0c;默认源太慢&#xff0c;换成可用的阿里云的源 2.apt-get failed to …问题 3.图形化异常问题 4.get unmet dependence 问题 5. 鼠标光标消失和鼠标变成叉叉问题。 解决方…

【计算机网络_应用层】https协议——加密和窃密的攻防

文章目录 1.https协议的介绍2. 加密和解密2.1 什么是加密2.2 常见的加密方式2.2.1 对称加密2.2.2 非对称加密 2.3 数据摘要&#xff08;数据指纹&#xff09;2.4 数字签名 3. https协议的加密和解密方案一&#xff1a;使用对称加密&#xff08;❌&#xff09;方案二&#xff1a…

搜维尔科技:动作捕捉与数字时尚:Wondar Studios欧莱雅项目

来自意大利的Wondar Studios工作室&#xff0c;是一家制作与动作捕捉技术相关软件和内容的公司&#xff0c;其出品的三维角色动画均由专业动捕系统真实录制制作。 我们很高兴与大家分享Wondar Studios最新的动捕项目&#xff0c;该项目带来了身临其境的虚拟现实体验。他们与巴…

加密流量分类torch实践4:TrafficClassificationPandemonium项目更新

加密流量分类torch实践4&#xff1a;TrafficClassificationPandemonium项目更新 更新日志 代码已经推送开源至露露云的github&#xff0c;如果能帮助你&#xff0c;就给鼠鼠点一个star吧&#xff01;&#xff01;&#xff01; 3/10号更新 流量预处理更新 增加了基于splitCa…

JavaScript基础6之执行上下文、作用域链、函数创建、函数激活、checkScope的执行过程、闭包、this

JavaScript基础 执行上下文执行上下文中的属性变量对象全局上下文的变量对象函数上下文执行过程进入执行上下文代码执行思考题 作用域链函数创建函数激活checkScope的执行过程总结 闭包分析闭包 this 执行上下文 执行上下文中的属性 每一个执行上下文都有三个核心属性 变量对…

03-安装配置jenkins

一、安装部署jenkins 1&#xff0c;上传软件包 为了方便学习&#xff0c;本次给大家准备了百度云盘的安装包 链接&#xff1a;https://pan.baidu.com/s/1_MKFVBdbdFaCsOTpU27f7g?pwdq3lx 提取码&#xff1a;q3lx [rootjenkins ~]# rz -E [rootjenkins ~]# yum -y localinst…

Linux学习:权限

目录 1. shell命令的工作原理与存在意义1.1 shell命令解释器存在的意义1.2 shell解释器的工作原理 2. Linux操作系统&#xff1a;用户2.1 什么是用户2.2 用户的切换操作2.3 用户权限划分的意义 3. Linux中权限的种类和意义3.1 什么是权限3.2 sudo指令与短暂提权 4. 文件类型与文…

03-快速上手RabbitMQ的5种消息模型

RabbitMQ RabbitMQ是基于Erlang语言开发的开源消息通信中间件,有几个常见概念 connections(连接): 将来publisher(消息的发送者)或者consumer(消息的接收者)都需要先与MQ建立连接 channel(通道): 建立连接后需要创建通道,生产者和消费者就是基于通道完成消息的发送和接收 ex…

网络安全:OpenEuler 部署 jumpserver 堡垒机

目录 一、实验 1.环境 2.OpenEuler 部署 jumpserver 堡垒机 3.OpenEuler 使用 jumpserver 堡垒机&#xff08;管理Linux&#xff09; 4.OpenEuler 使用 jumpserver 堡垒机&#xff08;管理Windows&#xff09; 二、问题 1.jumpserver 安装报错 一、实验 1.环境 &#x…

linux paddle For C++环境搭建

paddle介绍 Paddle是类似tesseract的文字识别ocr。因为tesseract-ocr的中文识别效果不好。因此才准备安装Paddle。Paddle最方便的安装方式的使用Python的包管理安装。pip3 install paddlepaddle。但我使用了一下感觉还是用C更加方便&#xff0c;QT OpenCV Paddle应当还不错。…

[Spark SQL]Spark SQL读取Kudu,写入Hive

SparkUnit Function&#xff1a;用于获取Spark Session package com.example.unitlimport org.apache.spark.sql.SparkSessionobject SparkUnit {def getLocal(appName: String): SparkSession {SparkSession.builder().appName(appName).master("local[*]").getO…

1.下载安装ESP32开发环境ESP-IDE

ESP32简介 ESP32介绍 说到ESP32&#xff0c;首先ESP32不是一个芯片&#xff0c;ESP32是一个系列芯片&#xff0c; 是乐鑫自主研发的一系列芯片微控制器。它主要的功能就是支持WiFi和蓝牙&#xff0c; ESP32指的是ESP32裸芯片。但是&#xff0c;“ESP32”一词通常指ESP32系列芯…

python基础练习 特殊回文数

资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 123321是一个非常特殊的数&#xff0c;它从左边读和从右边读是一样的。   输入一个正整数n&#xff0c; 编程求所有这样的…

RocketMQ、Kafka、RabbitMQ 消费原理,顺序消费问题【图文理解】

B站视频地址 文章目录 一、开始二、结果1、RocketMQ 消费关系图1-1、queue和consumer的关系1-2、consumer 和线程的关系 2、Kafka 消费关系图1-1、partitions和consumer的关系1-2、consumer 和线程的关系 3、RabbitMQ 消费关系图1-1、queue和consumer的关系1-2、consumer 和线程…

Python+Django+Html网页前后端指纹信息识别

程序示例精选 PythonDjangoHtml网页前后端指纹信息识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoHtml网页前后端指纹信息识别》编写代码&#xff0c;代码整洁&#xff0…

深入理解Java多线程与线程池:提升程序性能的利器

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、实现多线程 1.1. 继承Thread类 1.2. 实现Runnab…

WPF(1)的MVVM的数据驱动学习示例

MVVM Model:数据模型、View 界面、ViewModel 业务逻辑处理 项目结构 界面数据绑定 <Window x:Class"WpfApp1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/x…

opencv人脸识别实战3:多线程和GUI界面设计(PyCharm实现)

一、多线程设计 1、在一个新线程中调用了 scan_face() 函数来进行人脸识别操作。根据识别结果&#xff0c;更新界面显示结果&#xff0c;最后释放资源。 def f_scan_face_thread():var.set(刷脸)ans scan_face()if ans 0:print("最终结果&#xff1a;无法识别")va…