赞
踩
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 // 是否是叶子节点
}
关键字和子结点的关系
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*t-1个),此时就不能再插入了,所以引入一个分裂节点的操作,将一个满节点从中间分裂为两个,将中间的关键字提升到父节点中,分裂后的两个节点各有t-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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。