《算法导论》第18章 B树

源码地址

1. B树的定义

B树示意图:

B树示意图

// 保存在B树中的关键字,同时也可以保存其他数据
type Key interface {
    CompareTo(other Key) int
    String() string
}

type BTree struct {
    root *BTreeNode
    t    int // 最小度数,除根节点外的内部节点至少有t个孩子,至多2t个孩子
}

type BTreeNode struct {
    keys     []Key        // 关键字
    children []*BTreeNode // 孩子节点
    isLeaf   bool         // 是否是叶子节点
}
  • 关键字是以升序排列的(k[0] <= k[1] <= … <= k[n-1])
  • 关键字和子结点的关系

    NODE1的关键字都比k1小或等于,NODE2的关键字介于k1和k2之间,NODE3的关键字介于k2和k3之间,NODE4的关键字大于k3。

    关键字的个数用树的最小度数t来衡量,除根节点外的结点最少t-1个关键字,最多2t-1个,相对应的,除根节点外的结点最少t个孩子,最多2t个。

    关键字达到2t-1个时称该结点是满的。

  • 每个叶子结点都有相同的深度,即树高h。

    如果n(关键字个数)>=1,对任意一个包含n个关键字,高度为h,最小度数t>=2的B树:

    h从0开始计算

2. B树上的操作

2.1 插入关键字

如果一个节点是满的(关键字个数为2*t-1个),此时就不能再插入了,所以引入一个分裂节点的操作,将一个满节点从中间分裂为两个,将中间的关键字提升到父节点中,分裂后的两个节点各有t-1个关键字。

2.1.1 分裂满节点

假设t==3, x.child[i]指向的节点是一个满节点:

将y节点分裂成两个,y和z,各有t-1个关键字和t个子节点

将z节点当做x的第i+1个子节点,原来的child[i+1]及之后的子节点依次后移

将中间关键字k2当做x的keys[i],原来的keys[i]及之后的关键字依次后移

相关代码:

// splitChild分裂x的第i个子节点,x是非满的内部节点,x的children[i]已满,现在要分裂children[i]节点
func (t *BTree) splitChild(x *BTreeNode, i int) {
    z := t.allocateNode() // 分裂出来的节点
    y := x.children[i]    // z将是y的兄弟节点
    z.isLeaf = y.isLeaf
    d := t.t

    // y后半部分关键字分给z
    // y和z各有d-1个关键字,y为keys[0..d-1),z为keys[d,2d-1),keys[d-1]被提到父节点中
    for j := d; j < 2*d-1; j++ {
        z.keys = append(z.keys, y.keys[j])
    }
    upKey := y.keys[d-1] // 将要提升的关键字
    y.keys = y.keys[0 : d-1]

    // 如果y不是叶子,将y后半部分的孩子节点也分给z,分t个
    if !y.isLeaf {
        for j := d; j < 2*d; j++ {
            z.children = append(z.children, y.children[j])
        }
        y.children = y.children[0:d]
    }

    // 将z插入到x.children中
    // y是x.children[i],那么z现在是x.children[i+1]
    x.children = append(x.children, nil)
    for j := len(x.children) - 1; j > i+1; j-- {
        // x有n个关键字,必然有n+1个子结点
        x.children[j] = x.children[j-1]
    }
    x.children[i+1] = z

    // 将提升上来的关键字插入到x.keys中
    // 分裂前y中所有关键字都比x.keys[i]小,分裂后提升上来的关键字也比x.keys
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值