• 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 
    #include 
    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库中自取(包含源代码)

  • 相关阅读:
    「架构师合集」
    Docker从入门到进阶之进阶操作(3) —— 使用 supermin5来构建镜像
    洛谷千题详解 | P1016 [NOIP1999 提高组] 旅行家的预算【C++、Java、Python语言】
    数据预处理
    【三维目标检测】CenterPoint(一)
    怎样提升小程序日活?签到抽奖可行吗?
    SpringSecurity学习笔记(一)springSecurity的整体架构
    spring redis 工具类
    【新学期、新Flag】例文:我的新学期Flag
    FineBI 项目资源迁移
  • 原文地址:https://blog.csdn.net/hellmorning/article/details/139828264