B树(数据结构篇)

数据结构之B树

B-树(B-tree)

概念

  • B-树是一个非二叉树多路平衡查找树(数据有序),是一颗所有数据都存储在树叶节点上的树,不一定存储具体的数据,也可以是指向包含数据的记录的指针或地址

  • 对于**阶为M(子节点数量在2和M之间)**的B-树具有一下结构特性:

    1. 树的根节点或者叶子节点,或者子节点数的范围为[2,M]
    2. B树每个结点关键字数量为[ceil(2/M)-1,M-1]
    3. 除根外,所有非树叶节点的子节点数在[ceil(2/M),M]之间,ceil为向上取整函数
    4. 所有的树叶都在相同的深度上
  • 非叶子节点也有存储关键词的地方,这个地方是来表示范围的(如果要查找的数据小于该关键字就找关键字左边的子节点数据,大于就右边的子节点数据),如果叶子节点数据按照升序排列,则非叶子节点的关键词有m-1个(m为该非叶子节点的子节点个数),如果按照降序排列,则非叶子节点的关键字有m个,例如图4-58为升序排列的B树

    image

插入操作

  • 插入在叶子节点进行

  • 向B树中插入数据,根据非叶子节点的关键字找到正确的存储数据的叶子节点

  • 如果节点内的数据小于M-1(M为阶数),就根据排列规则插入;如果节点能够存储的数据已经满了,就进行分裂节点(叶子节点)

    分裂节点操作步骤:

    1. 先看看该叶子节点的关键字数量是否小于M-1(M为阶数)
    2. 按顺序插入进去,节点的关键字数量如果大于M-1,就将该叶子节点分裂成两个叶子节点,两个叶子节点的数据各占原叶子节点的一半中间的关键字(数据)作为根节点的关键字剩下分成两部分的节点作为其(中间关键字形成的根节点)左右节点。当根节点大于M-1的时候,就分裂根节点!
    3. 如果小于则根据插入的关键字大小按顺序插入。

删除操作

  1. 通过递归找到指定删除的节点

  2. 删除的关键字在非叶子节点上,就将其左边的指向叶子节点中的最大值跟要删除的关键字互换,就递归进去删除最大值。

  3. 删除的关键字在叶子节点上

    1. 叶子节点的关键字个数大于ceil(M/2-1),直接删除
    2. 叶子节点的关键字个数等于ceil(M/2-1),向父节点借关键字
  4. 递归返回上一层后检查该节点的关键字数,如果小于ceil(M/2-1),就向父节点借关键字,合并该关键字的左右节点,然后不断返回上一层,不断地检查,向父节点借关键字,合并子节点直到返回到根节点。

代码:

#include <iostream>
#include <vector>
using namespace std;

struct btree{
    int level;  //树的阶数
    vector<int>keys;  //关键字数组
    vector<btree*>child; //子节点数组
    int keynum;   //节点的关键字数目
    btree* parent;    //子节点的父节点
};

//创造节点
btree* createNode(int level){
    auto p=new btree;
    p->level=level;
    p->keynum=0;
    p->parent= nullptr;
    for(int i=0;i<=p->level;i++){
        p->child.push_back(nullptr);
        p->keys.push_back(0);
    }
    return p;
}

//查找指定值,返回一个包含节点指针,和指定值的位置的对组
pair<btree*,int> find(btree* root,int key){
    int i;
    for(i=root->keynum;i>0;i--){
        if(key<root->keys[i]){
            continue;
        }else if(key>root->keys[i]){
            break;
        }else{
            return make_pair(root,i);
        }
    }
    pair<btree*,int>p=find(root->child[i],key);
    return p;
}

//分裂节点
void splitNode(btree* &root,btree* p,int index){
    if(p!=nullptr){
        if(p->keynum==p->level){
            //分裂节点
            int mid=p->keynum/2+p->keynum%2;
            btree* newnode= createNode(p->level);
            newnode->parent=p->parent;
            for(int j=root->keynum;j>index-1;j--){
                root->keys[j+1]=root->keys[j];
            }
            root->keys[index]=p->keys[mid];
            root->keynum++;
            for(int j=mid+1;j<=p->keynum;j++){
                newnode->keys[j-mid]=p->keys[j];
                newnode->keynum++;
            }
            p->keynum=p->keynum-newnode->keynum-1;
            int k;
            for(k=root->level-1;k>index-1;k--){
                root->child[k+1]=root->child[k];
            }
            k++;
            root->child[k]=newnode;
        }
    }
    if(root->keynum==root->level) {
        btree *newchild = createNode(root->level);
        int mid = root->keynum / 2 + root->keynum % 2;
        for (int i = mid + 1; i <= root->level; i++) {
            newchild->keys[i - mid] = root->keys[i];
            newchild->keynum++;
        }
        for (int j = newchild->keynum; j <= newchild->level; j++) {
            if(root->child[j])
                root->child[j]->parent=newchild;
            newchild->child[j - newchild->keynum] = root->child[j];
            root->child[j] = nullptr;
        }
        if (root->parent == nullptr) {
            btree *newnode = createNode(root->level);
            newnode->keys[1] = root->keys[mid];
            newnode->keynum++;
            root->keynum = root->level - newchild->keynum - 1;
            newnode->child[0] = root;
            newnode->child[1] = newchild;
            root->parent = newnode;
            newchild->parent = newnode;
            root = newnode;
        } else {
            newchild->parent = root->parent;
            root->keynum = root->level - newchild->keynum - 1;
            int a = root->parent->keynum;
            while (a > 0 && root->keys[mid] < root->parent->keys[a]) {
                root->parent->keys[a + 1] = root->parent->keys[a];
                root->parent->child[a + 1] = root->parent->child[a];
                a--;
            }
            a++;
            root->parent->keys[a] = root->keys[mid];
            root->parent->keynum++;
            root->parent->child[a] = newchild;
        }
    }
}

//插入节点
btree* insert(btree* &root,int key){
    if(0==root->keynum){
        root->keys[1]=key;
        root->keynum++;
        return root;
    }
    int index=root->keynum;
    while (index>0&&key<root->keys[index]){
        root->keys[index+1]=root->keys[index];
        index--;
    }
    index++;
    if(root->child[0]!=nullptr){
        btree* p;
        if(index==root->keynum){
            p=root->child[index+1];
        }else{
            p=root->child[index-1];
        }
        if(root->child[0]->child[0]!=nullptr){
            p= insert(p,key);
        }else if(root->child[0]->child[0]==nullptr){
            int i=p->keynum;
            while (i>0&&key<p->keys[i]){
                p->keys[i+1]=p->keys[i];
                i--;
            }
            i++;
            p->keys[i]=key;
            p->keynum++;
        }
        splitNode(root,p,index);
    }else{
        root->keys[index]=key;
        root->keynum++;
        splitNode(root, nullptr,-1);
    }
    return root;
}

//查找最大值
int findmax(btree* root){
    if(nullptr==root){
        return 0;
    }else if(root->child[0]!= nullptr){
        return findmax(root->child[root->keynum]);
    }
    return root->keys[root->keynum];
}

//合并节点
void merge(btree* &root,int key,int min,int n){
    int n1 = root->child[n-1]->keynum;
    int n2 = root->child[n]->keynum;
    if (n1 > min) {
        for (int j = n2; j > 0; j--) {
            root->child[n]->keys[j + 1] = root->child[n]->keys[j];
            root->child[n]->child[j + 1] = root->child[n]->child[j];
        }
        root->child[n]->child[1] = root->child[n]->child[0];
        root->child[n]->keys[1] = root->keys[n];
        root->keys[n]=root->child[n-1]->keys[n1];
        root->child[n]->child[0] = root->child[n-1]->child[n1];
        root->child[n-1]->child[n1] = nullptr;
        root->child[n-1]->child[0]->parent = root->child[n-1];
        root->child[n-1]->keynum--;
        root->child[n-1]->keys[n1] = NULL;
        root->child[n]->keynum++;
    } else if (n2 > min) {
        root->child[n-1]->keys[n1+1]=root->keys[n];
        root->keys[n]=root->child[n]->keys[1];
        root->child[n-1]->child[n1 + 1] = root->child[n]->child[0];
        root->child[n-1]->child[n1 + 1]->parent = root->child[n-1];
        root->child[n-1]->keynum++;
        for (int j = 1; j < n2; j++) {
            root->child[n]->keys[j] = root->child[n]->keys[j + 1];
            root->child[n]->child[j - 1] = root->child[n]->child[j];
        }
        root->child[n]->child[n2-1]=root->child[n]->child[n2];
        root->child[n]->keys[n2] = NULL;
        root->child[n]->child[n2] = nullptr;
        root->child[n]->keynum--;
    } else {
        root->child[n-1]->keys[n1+1]=root->keys[n];
        root->child[n-1]->keynum++;
        int n3 = n2 + n1+1;
        for (int j = n1 + 2; j <= n3; j++) {
            root->child[n-1]->keys[j] = root->child[n]->keys[j - n1-1];
            root->child[n-1]->child[j-1]=root->child[n]->child[j];
            root->child[n-1]->keynum++;
        }
        root->child[n]=nullptr;
        int index = root->keynum;
        while (index > n && key < root->keys[index]) {
            root->keys[index-1]=root->keys[index];
            root->child[index-1]=root->child[index];
            index--;
        }
        root->child[root->keynum]= nullptr;
        root->keynum--;
        if(root->parent== nullptr&&root->keynum==0){
            root->child[0]->parent= nullptr;
            root=root->child[0];
        }
    }
}

//删除节点
void del(btree* &root,int key){
    if(nullptr==root){
        return;
    }else{
        int i;
        for(i=root->keynum;i>0;i--){
            if(key<root->keys[i]){
                continue;
            }else if(key>root->keys[i]){
                del(root->child[i],key);
            }else{
                break;
            }
        }
        int min=(root->level/2+root->level%2)-1;
        if(0==i){
            if(root->child[i]->keynum>=min&&root->child[i+1]->keynum>=min){
                del(root->child[i],key);
            }
            i++;
        }
        if(root->child[0]!= nullptr){
            if(root->keynum>=min){
                if(root->keys[i]==key){
                    int temp= findmax(root->child[i-1]);
                    root->keys[i]=temp;
                    del(root->child[i-1],temp);
                    merge(root,key,min,i);
                }else if(key<root->keys[i]){
                    if(root->child[i-1]->keynum<min){
                        merge(root,key,min,i);
                    }
                }else{
                    if(root->child[i]->keynum<min){
                        merge(root,key,min,i);
                    }
                }
            }else{
                merge(root,key,min,i);
            }
        }else{
            int j;
            for(j=1;j<root->keynum;j++){
                if(root->keys[j]==key){
                    break;
                }
            }
            for(int d=j;d<root->keynum;d++){
                root->keys[d]=root->keys[d+1];
            }
            root->keys[root->keynum]=NULL;
            if(root->keynum>min){
                root->keynum--;
            }else{
                root->keynum--;
                int index=root->parent->keynum;
                for(int k=root->keynum;k>0;k--){
                    root->keys[k+1]=root->keys[k];
                }
                while (index>0&&key<=root->parent->keys[index]){
                    index--;
                }
                if(0==index){
                    root->keys[root->keynum+1]=root->parent->keys[1];
                }else{
                    root->keys[root->keynum+1]=root->parent->keys[index];
                }
            }
        }
    }
}

//中序遍历
void inorderprint(btree* root){
    if(nullptr!=root){
        int i;
        for(i=0;i<root->keynum;i++){
            if(root->child[i]!= nullptr){
                inorderprint(root->child[i]);
            }
            cout<<root->keys[i+1]<<" ";
        }
        if(root->child[i]!= nullptr)
            inorderprint(root->child[i]);
    }
}

尾言

完整版笔记也就是数据结构与算法专栏完整版可到我的博客进行查看,或者在github库中自取(包含源代码)

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值