深入探索B树:结构、性质与应用

news2024/9/9 4:29:17

深入探索B树:结构、性质与应用

目录

  1. 简介
  2. B树的结构
    • 节点定义
    • 树的高度
    • 节点的属性
  3. B树的性质
    • 平衡性
    • 节点容量
    • 查找性能
  4. B树的基本操作
    • 插入操作
    • 删除操作
    • 查找操作
  5. B树的应用
    • 数据库
    • 文件系统
  6. B树的实现
    • Python实现B树
    • Java实现B树

简介

B树(B-tree)是一种自平衡树数据结构,广泛应用于数据库和文件系统中以提高查找、插入和删除操作的效率。B树的设计目标是减少访问磁盘的次数,因此在处理大规模数据时表现出色。本文将深入探讨B树的结构、性质、操作及其应用,并提供详细的代码实现示例。

B树的结构

节点定义

B树由若干节点组成,每个节点可以包含多个键和指向子节点的指针。一个典型的B树节点结构如下:

  • 一个节点包含最多m个子节点(称为m阶B树)。
  • 每个节点至少包含ceil(m/2)个子节点,根节点例外。
  • 每个节点包含k个键,其中ceil(m/2) - 1 <= k <= m - 1

树的高度

B树的高度与其包含的元素数量成对数关系,这保证了操作的时间复杂度为O(log n)。由于每个节点包含多个键,B树的高度通常较低,从而减少了磁盘访问次数。

节点的属性

每个B树节点包含以下属性:

  • n:节点中包含的键数量。
  • keys[]:大小为n的数组,存储键值。
  • C[]:大小为n+1的数组,存储子节点指针。
  • leaf:布尔值,指示节点是否为叶子节点。

B树的性质

平衡性

B树是一种平衡树,其所有叶子节点都位于同一层级。这保证了从根节点到任意叶子节点的路径长度相同,从而提供稳定的查找性能。

节点容量

B树的节点容量由其阶数决定。对于一个m阶B树:

  • 每个节点最多包含m-1个键。
  • 每个节点至少包含ceil(m/2) - 1个键,根节点至少包含1个键。
  • 除根节点外,每个内部节点至少包含ceil(m/2)个子节点。

查找性能

由于B树的高度较低且节点包含多个键,查找操作可以快速定位到目标节点。查找操作的时间复杂度为O(log n),其中n为树中包含的键数量。

B树的基本操作

插入操作

插入操作的关键在于保持B树的性质,即确保每个节点的键数量在允许范围内。如果插入导致节点超出容量,则需要进行分裂操作。

插入操作的步骤如下:

  1. 在叶子节点中插入键。
  2. 如果叶子节点超出容量,则将其分裂为两个节点,并将中间键上移到父节点。
  3. 如果父节点超出容量,则重复分裂过程,直到树重新平衡。
插入操作示例代码
class BTreeNode:
    def __init__(self, t, leaf=False):
        self.t = t  # 最小度数
        self.leaf = leaf  # 是否为叶子节点
        self.keys = []  # 存储键值
        self.children = []  # 存储子节点

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

    def insert(self, k):
        root = self.root
        if len(root.keys) == 2 * self.t - 1:
            temp = BTreeNode(self.t, False)
            self.root = temp
            temp.children.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)
            while i >= 0 and k < x.keys[i]:
                x.keys[i + 1] = x.keys[i]
                i -= 1
            x.keys[i + 1] = k
        else:
            while i >= 0 and k < x.keys[i]:
                i -= 1
            i += 1
            if len(x.children[i].keys) == 2 * self.t - 1:
                self.split_child(x, i)
                if k > x.keys[i]:
                    i += 1
            self.insert_non_full(x.children[i], k)

    def split_child(self, x, i):
        t = self.t
        y = x.children[i]
        z = BTreeNode(t, y.leaf)
        x.children.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.children = y.children[t:(2 * t)]
            y.children = y.children[0:(t - 1)]

# 测试插入操作
b_tree = BTree(3)
for value in [10, 20, 5, 6, 12, 30, 7, 17]:
    b_tree.insert(value)

# 打印B树结构
def print_btree(node, level=0):
    print("Level", level, " ", len(node.keys), ":", node.keys)
    if not node.leaf:
        for child in node.children:
            print_btree(child, level + 1)

print_btree(b_tree.root)

删除操作

删除操作相对复杂,需要处理多种情况以保持B树的性质:

  1. 如果键在叶子节点中,直接删除。
  2. 如果键在内部节点中:
    • 用前驱或后继键替换,然后递归删除前驱或后继键。
    • 合并左右子节点,然后递归删除。
  3. 如果键不在节点中,递归查找直到找到目标键并进行删除。
  4. 在删除过程中,保持节点的键数量在允许范围内,如需合并或借用键则进行相应操作。
删除操作示例代码
class BTree:
    # ...之前的代码...

    def delete(self, k):
        self.delete_recursive(self.root, k)
        if len(self.root.keys) == 0:
            if len(self.root.children) > 0:
                self.root = self.root.children[0]
            else:
                self.root = BTreeNode(self.t, True)

    def delete_recursive(self, node, k):
        t = self.t
        i = 0
        while i < len(node.keys) and k > node.keys[i]:
            i += 1

        if i < len(node.keys) and k == node.keys[i]:
            if node.leaf:
                node.keys.pop(i)
            else:
                if len(node.children[i].keys) >= t:
                    pred = self.get_predecessor(node, i)
                    node.keys[i] = pred
                    self.delete_recursive(node.children[i], pred)
                elif len(node.children[i + 1].keys) >= t:
                    succ = self.get_successor(node, i)
                    node.keys[i] = succ
                    self.delete_recursive(node.children[i + 1], succ)
                else:
                    self.merge(node, i)
                    self.delete_recursive(node.children[i], k)
        elif not node.leaf:
            if len(node.children[i].keys) < t:
                if i != 0 and len(node.children[i - 1].keys) >= t:
                    self.borrow_from_prev(node, i)
                elif i != len(node.children) - 1 and len(node.children[i + 1].keys) >= t:
                    self.borrow_from_next(node, i)
                else:
                    if i != len(node.children) - 1:
                        self.merge(node, i)
                    else:
                        self.merge(node, i - 1)
            self.delete_recursive(node.children[i], k)

    def get_predecessor(self, node, idx):
        cur = node.children[idx]


        while not cur.leaf:
            cur = cur.children[len(cur.keys)]
        return cur.keys[len(cur.keys) - 1]

    def get_successor(self, node, idx):
        cur = node.children[idx + 1]
        while not cur.leaf:
            cur = cur.children[0]
        return cur.keys[0]

    def merge(self, node, idx):
        child = node.children[idx]
        sibling = node.children[idx + 1]
        t = self.t
        child.keys.append(node.keys.pop(idx))
        child.keys.extend(sibling.keys)
        if not child.leaf:
            child.children.extend(sibling.children)
        node.children.pop(idx + 1)

    def borrow_from_prev(self, node, idx):
        child = node.children[idx]
        sibling = node.children[idx - 1]
        child.keys.insert(0, node.keys[idx - 1])
        if not child.leaf:
            child.children.insert(0, sibling.children.pop())
        node.keys[idx - 1] = sibling.keys.pop()

    def borrow_from_next(self, node, idx):
        child = node.children[idx]
        sibling = node.children[idx + 1]
        child.keys.append(node.keys[idx])
        if not child.leaf:
            child.children.append(sibling.children.pop(0))
        node.keys[idx] = sibling.keys.pop(0)

# 测试删除操作
b_tree = BTree(3)
for value in [10, 20, 5, 6, 12, 30, 7, 17]:
    b_tree.insert(value)

print("初始B树结构:")
print_btree(b_tree.root)

b_tree.delete(6)
print("删除6后的B树结构:")
print_btree(b_tree.root)

b_tree.delete(13)
print("删除13后的B树结构:")
print_btree(b_tree.root)

b_tree.delete(7)
print("删除7后的B树结构:")
print_btree(b_tree.root)

b_tree.delete(4)
print("删除4后的B树结构:")
print_btree(b_tree.root)

b_tree.delete(2)
print("删除2后的B树结构:")
print_btree(b_tree.root)

b_tree.delete(16)
print("删除16后的B树结构:")
print_btree(b_tree.root)

查找操作

查找操作相对简单,通过逐层遍历节点中的键值,可以快速定位目标键。

查找操作示例代码
class BTree:
    # ...之前的代码...

    def search(self, k, x=None):
        if x is None:
            x = self.root
        i = 0
        while i < len(x.keys) and k > x.keys[i]:
            i += 1
        if i < len(x.keys) and k == x.keys[i]:
            return (x, i)
        elif x.leaf:
            return None
        else:
            return self.search(k, x.children[i])

# 测试查找操作
b_tree = BTree(3)
for value in [10, 20, 5, 6, 12, 30, 7, 17]:
    b_tree.insert(value)

result = b_tree.search(6)
if result:
    print("Found key 6 at node:", result[0].keys, "index:", result[1])
else:
    print("Key 6 not found.")

result = b_tree.search(15)
if result:
    print("Found key 15 at node:", result[0].keys, "index:", result[1])
else:
    print("Key 15 not found.")

B树的应用

数据库

B树广泛应用于数据库系统中,以实现高效的索引结构。B树的平衡性和较低的高度保证了在大数据集上的快速查找、插入和删除操作。数据库中的B树变种包括B+树和B*树,提供了更高效的性能和更强的稳定性。

文件系统

B树还被用于文件系统中的目录管理和文件索引。例如,NTFS文件系统使用B+树来存储和管理文件目录,提高了文件检索和管理的效率。B树的多级结构使其非常适合存储大规模数据,同时保持高效的查找性能。

B树是一种自平衡的树数据结构,常用于数据库和文件系统中,用于存储排序的数据并支持高效的插入、删除和查找操作。下面是B树的Python和Java实现示例。

Python 实现 B 树

class BTreeNode:
    def __init__(self, t, leaf=False):
        self.t = t  # 最小度数 (t)
        self.leaf = leaf  # 是否是叶子节点
        self.keys = []  # 节点中的键
        self.children = []  # 节点中的子树

class BTree:
    def __init__(self, t):
        self.root = BTreeNode(t, leaf=True)
        self.t = t  # 最小度数 (t)

    def traverse(self):
        self._traverse(self.root)

    def _traverse(self, node):
        i = 0
        while i < len(node.keys):
            if not node.leaf:
                self._traverse(node.children[i])
            print(node.keys[i], end=' ')
            i += 1
        if not node.leaf:
            self._traverse(node.children[i])

    def search(self, k):
        return self._search(self.root, k)

    def _search(self, node, k):
        i = 0
        while i < len(node.keys) and k > node.keys[i]:
            i += 1
        if i < len(node.keys) and node.keys[i] == k:
            return node
        if node.leaf:
            return None
        return self._search(node.children[i], k)

    def insert(self, k):
        root = self.root
        if len(root.keys) == (2 * self.t - 1):
            s = BTreeNode(self.t, leaf=False)
            self.root = s
            s.children.append(root)
            self._split_child(s, 0)
            self._insert_non_full(s, k)
        else:
            self._insert_non_full(root, k)

    def _insert_non_full(self, node, k):
        i = len(node.keys) - 1
        if node.leaf:
            node.keys.append(None)
            while i >= 0 and k < node.keys[i]:
                node.keys[i + 1] = node.keys[i]
                i -= 1
            node.keys[i + 1] = k
        else:
            while i >= 0 and k < node.keys[i]:
                i -= 1
            i += 1
            if len(node.children[i].keys) == (2 * self.t - 1):
                self._split_child(node, i)
                if k > node.keys[i]:
                    i += 1
            self._insert_non_full(node.children[i], k)

    def _split_child(self, node, i):
        t = self.t
        y = node.children[i]
        z = BTreeNode(t, leaf=y.leaf)
        node.children.insert(i + 1, z)
        node.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.children = y.children[t:(2 * t)]
            y.children = y.children[0:t]

# 示例
bt = BTree(3)
bt.insert(10)
bt.insert(20)
bt.insert(5)
bt.insert(6)
bt.insert(15)
bt.traverse()  # 输出: 5 6 10 15 20

Java 实现 B 树

import java.util.ArrayList;
import java.util.Collections;

class BTreeNode {
    int t;
    ArrayList<Integer> keys = new ArrayList<>();
    ArrayList<BTreeNode> children = new ArrayList<>();
    boolean leaf = true;

    BTreeNode(int t, boolean leaf) {
        this.t = t;
        this.leaf = leaf;
    }
}

class BTree {
    private BTreeNode root;
    private int t;

    BTree(int t) {
        this.root = new BTreeNode(t, true);
        this.t = t;
    }

    void traverse() {
        traverse(root);
    }

    private void traverse(BTreeNode node) {
        int i = 0;
        while (i < node.keys.size()) {
            if (!node.leaf) {
                traverse(node.children.get(i));
            }
            System.out.print(node.keys.get(i) + " ");
            i++;
        }
        if (!node.leaf) {
            traverse(node.children.get(i));
        }
    }

    BTreeNode search(int k) {
        return search(root, k);
    }

    private BTreeNode search(BTreeNode node, int k) {
        int i = 0;
        while (i < node.keys.size() && k > node.keys.get(i)) {
            i++;
        }
        if (i < node.keys.size() && node.keys.get(i) == k) {
            return node;
        }
        if (node.leaf) {
            return null;
        }
        return search(node.children.get(i), k);
    }

    void insert(int k) {
        BTreeNode r = root;
        if (r.keys.size() == 2 * t - 1) {
            BTreeNode s = new BTreeNode(t, false);
            root = s;
            s.children.add(r);
            splitChild(s, 0);
            insertNonFull(s, k);
        } else {
            insertNonFull(r, k);
        }
    }

    private void insertNonFull(BTreeNode node, int k) {
        int i = node.keys.size() - 1;
        if (node.leaf) {
            node.keys.add(null);
            while (i >= 0 && k < node.keys.get(i)) {
                node.keys.set(i + 1, node.keys.get(i));
                i--;
            }
            node.keys.set(i + 1, k);
        } else {
            while (i >= 0 && k < node.keys.get(i)) {
                i--;
            }
            i++;
            if (node.children.get(i).keys.size() == 2 * t - 1) {
                splitChild(node, i);
                if (k > node.keys.get(i)) {
                    i++;
                }
            }
            insertNonFull(node.children.get(i), k);
        }
    }

    private void splitChild(BTreeNode node, int i) {
        int t = this.t;
        BTreeNode y = node.children.get(i);
        BTreeNode z = new BTreeNode(t, y.leaf);
        node.children.add(i + 1, z);
        node.keys.add(i, y.keys.get(t - 1));
        z.keys.addAll(y.keys.subList(t, 2 * t - 1));
        y.keys.subList(t - 1, y.keys.size()).clear();
        if (!y.leaf) {
            z.children.addAll(y.children.subList(t, 2 * t));
            y.children.subList(t, y.children.size()).clear();
        }
    }

    // 示例
    public static void main(String[] args) {
        BTree bt = new BTree(3);
        bt.insert(10);
        bt.insert(20);
        bt.insert(5);
        bt.insert(6);
        bt.insert(15);
        bt.traverse();  // 输出: 5 6 10 15 20
    }
}

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

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

相关文章

聚焦全局应用可用性的提升策略,详解GLSB是什么

伴随互联网的快速发展和全球化趋势的深入&#xff0c;企业对网络应用的需求日渐增长。为满足全球范围内用户大量的访问需求&#xff0c;同时解决容灾、用户就近访问以及全球应用交付等问题&#xff0c;GLSB&#xff08;全局负载均衡&#xff09;也因此应运而生。那么GLSB是什么…

Axure RP:打造动态交互的大屏可视化设计利器

Axure大屏可视化是指使用Axure RP这款原型设计工具来创建具有视觉冲击力和数据展示功能的大屏幕界面。Axure以其强大的交互设计和丰富的组件库&#xff0c;成为了实现大屏可视化的重要工具之一。以下是对Axure大屏可视化的详细阐述&#xff1a; 一、Axure在大屏可视化中的优势 …

​易能医药董事长易跃能博士荣获“湖湘药学领航奖”

近日&#xff0c;湖南省药学会主办的“湖南省药学会70周年庆典暨第六届湖南药学大会”在湖南长沙隆重召开。易能医药董事长易跃能博士荣获由湖南省药学会颁发的“湖湘药学领航奖”。此次“湖湘药学领航奖”由湖南药学大会学术委员会组织评选&#xff0c;湖南省全省仅有八个名额…

六、3 PWM 舵机代码

目录 1、通道选择 2、参数计算 3、代码部分 1、通道选择 PA1对应通道2 注意&#xff1a;同一个定时器不同通道输出PWM的特点 同一个定时器的不同通道输出的PWM&#xff0c;频率相同&#xff08;因为它们共用一个计数器&#xff09;&#xff0c;占空比可以各自设定&#xff…

Kubernetes 学习记录

https://note.youdao.com/ynoteshare/index.html?idbc7bee305611b52d6900ba209a92bd4d&typenote&_time1694072007342 概览 K8S官网文档&#xff1a;https://kubernetes.io/zh/docs/home/ K8S 是Kubernetes的全称&#xff0c;源于希腊语&#xff0c;意为“舵手”或“…

ITSS:IT服务工程师

证书亮点&#xff1a;适中的费用、较低的难度、广泛的应用范围以及专业的运维认证。 总体评价&#xff1a;性价比良好&#xff01; 证书名称&#xff1a;ITSS服务工程师 证书有效期&#xff1a;持续3年 培训要求&#xff1a;必须参加培训&#xff0c;否则将无法参与考试 发…

Aboboo一些操作

常用快捷键⌨ 快捷键/操作方式 功能 鼠标中键/Esc 进入/退出全屏 空格/Tab 暂停/恢复播放 左/右箭头 快退/快进 Ctrl-左/右箭头 30秒快退/快进 Alt-左/右箭头 60秒快退/快进 Ctrl-Alt-左/右箭头 播放速率调节 PageUp/PageDown 上一句/下一句 上下箭头/滚轮 …

WSL配置镜像网络使用本地端口调试Linux程序

一、安装WSL 二、配置WSL为镜像 在C:\Users\XXUser目录下添加.wslconfig文件 [wsl2] networkingModemirrored # 开启镜像网络 dnsTunnelingtrue # 开启 DNS Tunneling firewalltrue # 开启 Windows 防火墙 autoProxytrue # 开启自动同步代理重启WSL wsl --shutdown wsl三、…

计算机毕业设计选题推荐-音乐播放系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

计算机网络04

文章目录 IP 基本认识**IP 地址的基础知识****IP 地址的分类**无分类地址 CIDR公有 IP 地址与私有 IP 地址IP 地址与路由控制IP 分片与重组IPv6 基本认识IPv4 首部与 IPv6 首部 IP 协议相关技术DNS 域名解析ARP 与 RARP 协议DHCP 动态获取 IP 地址NAT 网络地址转换ICMP 互联网控…

NACOS保姆笔记(5)——Nacos的集群教程

前面我们介绍过: NACOS保姆笔记(1)——NACOS的安装和启动NACOS保姆笔记(2)——Spring Cloud Alibaba Nacos服务注册与发现以及负载均衡NACOS保姆笔记(3)——Spring Cloud Alibaba Nacos配置中心NACOS保姆笔记(4)——Spring Cloud Alibaba Nacos鉴权本篇主要介绍下Na…

5种IO模型简述

文章目录 前言什么是IO模型&#xff1f;阻塞IO非阻塞IO多路复用IO信号驱动IO异步IO 结语 前言 最近学netty&#xff0c;当然无法避免IO模型这部分知识。 我尽量用最简洁的语言来讲清楚这个东西。 什么是IO模型&#xff1f; 既然最近学netty&#xff0c;就拿它来举例子。 比如…

ITPUB专访 | 张宏波:一场关于编程语言速度与效率的深度对话

ITPUB专访 | 张宏波&#xff1a;一场关于编程语言速度与效率的深度对 随着 AI 大语言模型&#xff08;LLM&#xff09;不断突破和开源社区活跃程度达到前所未有的高度&#xff0c;以 OpenAI 的 GPT-4、Meta-LLaMA 等为代表的重量级产品和服务相继发布&#xff0c;AI 技术的蓬勃…

视频VIP收费会员播放帝国CMS模板HTML5自适应手机多种运营模式

采用帝国CMS最新版核心制作&#xff0c;自适应响应式手机平板浏览&#xff0c;手机浏览器非常舒服哦&#xff01;多种运营模式。用户中心逻辑和页面&#xff0c;都已经制作完整&#xff0c;可以搭建后稍微修改即可使用&#xff01; 模板特点&#xff1a; 支持多集和单集播放&…

【2024】Datawhale AI夏令营 Task2笔记——Baseline代码详细解读

【2024】Datawhale AI夏令营 Task2笔记——Baseline代码详细解读 本文对可完成赛事“逻辑推理赛道&#xff1a;复杂推理能力评估”初赛的baseline代码进行详细解读&#xff0c;该baseline代码由Datawhale AI夏令营提供&#xff0c;核心内容是调用灵积模型服务平台的大语言模型…

大众点评采集店铺全部推荐菜的全部信息

大众点评餐饮店铺的推荐菜是个好东西&#xff0c;蕴含的信息很丰富&#xff0c;可以用作店铺运营分析参考。 指定大众点评店铺&#xff0c;可以采集该店铺的全部推荐菜的全部信息&#xff0c;包括推荐菜的名字、价格、最近三个月推荐次数、推荐菜的评价、推荐菜的标签、菜品所…

从PyTorch官方的一篇教程说开去(5 - 神经网络 torch.nn)

神经网络长啥样&#xff1f;有没有四只眼睛八条腿&#xff1f; 借图镇楼 - 真的是非常经典&#xff0c;可以给下面的解释省掉很多力气。 分3个维度阐述 - 1&#xff09;输入数据集。假如你自己去微调一下大模型就知道&#xff0c;最开始的一步就是要准备&#xff08;足够大…

一个项目的坎坷一生

大家好&#xff0c;我是苍何。 目前呢&#xff0c;主要是负责部门的项目管理和团队管理相关工作&#xff0c;今天想和大家分享一下企业级标准的项目管理流程以及苍何的实践。 通过本文&#xff0c;能帮助你更快的在企业中上手项目并定位好自己的角色&#xff0c;别人一脸懵逼…

高效恢复误删文件:2024年数据恢复工具

现在都是互联网的时代&#xff0c;数据已成为我们生活与工作中不可或缺的一部分。很多时候我们都依赖电子设备来存储数据&#xff0c;这也造成来数据丢失风险的增加。这时候如果掌握了一些数据恢复软件&#xff0c;比如转转大师数据恢复软件这种就能对你的电子存储数据有保障。…

如何在 Windows 系统环境下安装 Tesseract OCR? ( •̀ ω •́ )✧

第一步&#xff1a;下载Tesseract OCR引擎安装包 &#x1f351; 访问Tesseract的GitHub发布页面&#xff08;https://github.com/tesseract-ocr/tesseract&#xff09;或第三方下载站点&#xff08;https://digi.bib.uni-mannheim.de/tesseract/&#xff09;&#xff0c;下载适…