B+树⚓︎
是一种多叉树
定义
A B+ tree of order M is a tree with following structural properties:
- Root: a leaf / has [2~M] children
- Nonleaf nodes (except root) : has [\(\lceil M/2 \rceil\) , M] children
- Leaf: same depth
如图为 M = 3 的情况
可以看出非叶子节点最多存储 M-1 个键值,其中第 i 个对应其第 i+1 个子树中存储的最小值

操作⚓︎
Find⚓︎
- 
与当前节点的所有子节点左边界值进行比较以找到应该在的区间,从而递归入相应子节点进行查找 
- 
如果不使用二分查找优化复杂度 \(O((M/\log{M})\log{N})\) ,否则 \(O(\log{N})\) 。 Insert 和 Delete 操作同理。 
Code
Node Find(int key, Node cur){
    int i = 0;
    while( i < cur.cntSon )
        if(key >= cur.keys[i] ) ++i;
    if( i == cur.cntkeys ) return EmptyNode;
    return Find(key, cur.son[i]);
}
Insert⚓︎
先递归到对应的区间,即叶节点,此时有几种情况:
- 
如果那个区间包含的数不超过 M - 1 个:直接插入进去就行 
- 
否则,需要对这个区间进行分裂,上层结构也需要有相应变化 - 
当前叶节点分裂成两个,各自拥有 \(\lfloor (M+1)/2 \rfloor\) 个数 
- 
递归处理父节点,直到找到一个儿子数量没到 \(M-1\) 的祖宗节点为止。如果直到根节点都不满足要求,重新建一个根节点并将原根节点分裂 
 
- 
下面给出一个大框架
沿着链向上处理分裂
void deal(int cur, int son0, int son1, int insert_val){
    if(!cur){
        Set(cur = root = ++cnt, 2, 1, 0);
        UpdateRelation(cur, son0, son1);
        T[cur].v[0] = insert_val;
        T[cur].fa = 0;
        return;
    }
    //处理掉父亲关系问题
    if(T[cur].totv < 2){
        InsData(T[cur].v, T[cur].v, &T[cur].totv, insert_val);
        int pos;
        for(pos = T[cur].totch; pos; --pos){
            if(T[cur].ch[pos - 1] == son0){
                T[cur].ch[pos] = son1;
                break;
            }
            T[cur].ch[pos] = T[cur].ch[pos - 1];
        }
        T[cur].totch++;
        T[son1].fa = cur;
        return;
    }
    // 分裂示意图
    //                                  /            \(v2)
    //   cur(v1~v3)              cur(v1)          newnode(v3)
    //  /   |   \        =>;      /    \            /      \
    // A    son   B              A     son0        son1    B
    //
    InsData(T[cur].v, b, &T[cur].totv, insert_val);
    Set(++cnt, 2, 1, 0);
    T[cnt].v[0] = b[2];
    Set(cur, 2, 1, 0);
    T[cur].v[0] = b[0];
    if(T[cur].ch[0] == son0) {
        UpdateRelation(cnt, T[cur].ch[1], T[cur].ch[2]);
        UpdateRelation(cur, son0, son1);
    }
    else if(T[cur].ch[1] == son0) {
        UpdateRelation(cnt, son1, T[cur].ch[2]);
        UpdateRelation(cur, T[cur].ch[0], son0);
    }
    else UpdateRelation(cnt, son0, son1);
    deal(T[cur].fa, cur, cnt, b[1]);
}
插入操作
int Insert(int cur, int val){
    if(T[cur].IsLeaf){
        if(Check(T[cur], val)) return 0;
        if(T[cur].totv < 3){
            InsData(T[cur].v, T[cur].v, &T[cur].totv, val);
        }
        else{
            InsData(T[cur].v, b, &T[cur].totv, val);
            Set(++cnt, 0, 2, 1);
            Set(cur, 0, 2, 1);
            for(int i = 0; i < 2; ++i){
                T[cur].v[i] = b[i];
                T[cnt].v[i] = b[2+i];
            }
            if(T[cur].fa)
                deal(T[cur].fa, cur, cnt, b[2]);
            else{
                Set(root = ++cnt, 2, 1, 0);
                T[root].v[0] = b[2];
                T[root].fa = 0;
                UpdateRelation(root, cur, cnt - 1);
            }
        }
        return 1;
    }
    int to_node = 0;
    for(int i = 0; i <= T[cur].totv; ++i)
        if(val >= T[cur].v[i]) ++to_node;
    return Insert(T[cur].ch[to_node], val);
}
Delete⚓︎
- 同样是递归到相应叶节点进行处理- 当删除掉当前值不会使叶节点包含值少于 \(\lceil M/2 \rceil\) ,直接删除
- 若删除后本节点不符合要求
- 若兄弟节点包含多于 \(\lceil M/2 \rceil\) 个值,可以从兄弟节点借一个,并更新父节点中的分隔值
- 否则与兄弟节点合并并删除父节点中的分隔值,递归处理父节点
 
作业题⚓︎
T1:节点数计算
判断:A 2-3 tree with 3 nonleaf nodes must have 18 keys at most.
Answer
T
Tips:叶节点指的是直接连向数值的节点而非数值本体
T2:插入与分裂操作

解析
结构大致是(懒得画图,自行意会一下)
[6]
[2,4][8]
[0,1][2,3][4,5][6,7][8,9]
T3:删除操作

解析
结构是
[4,6]
[1,2,3][4,5][6,7,8]
目前还没有写过删除的代码所以实现细节不明
T4:概念辨析

解析
注意只有根节点这种特例!!