B树及代码实现(c++)

泄露秘密 提交于 2020-02-16 19:16:35

这里推荐一篇图文并茂的好文章,
B树、B+树、B*树
这里我实现代码过于粗糙,应该有很多地方可以改进,
另外我并没有实现b+树以及b*树,但其实应该也不复杂,无非是在struct增加指向兄弟结点的指针,应该也容易实现:

/*
 * B 树
 * */
#include "iostream"
#include "vector"
#include <cstring>
#include <typeinfo>
#include <cmath>
#include <algorithm>

using namespace std;

//#define DEGREE 6;
const int DEGREE = 4;
int maxD = DEGREE - 1;
int minD = (int)std::ceil(DEGREE / 2.) - 1;/* NOLINT */

template <class T>
int getArrayLen(T& array)
{//使用模板定义一 个函数getArrayLen,该函数将返回数组array的长度
    return (sizeof(array) / sizeof(array[0]));
}
// TODO: 用预值,定义data数组
typedef struct BTnode {
//    int key;
    vector<int> data;
    vector<BTnode*> children;
    BTnode *father{};
}BTNode,*lpBNode;

void print(vector<int>& st){
    for(const auto& i:st){
        cout<<i<<" ";
    }
    cout<<endl;
}
int getPositonForF(lpBNode btn){
    lpBNode pf = btn->father;
    if(pf == nullptr)return 0;
    else {
        int i;
        for(i=0;i<(int)pf->children.size();i++){
            if(pf->children.at(i)==btn)
                break;
        }
        return i;
    }
}
void BtSplit(lpBNode& btr){
    // 超过maxD,进行分裂
    auto right = new BTNode;
    //  lpBNode right;

    int len = btr->data.size(); // 除了根结点外,n个关键值,一定会有n+1个指针
    //  int clen = btr->children.size();
    int rightStart = (int)std::ceil((float)len / 2.);//4,4
    // 移除数据
    for(int i=rightStart;i<len+1;i++){
        if(i<len) {
            right->data.push_back(btr->data.at(i));
        }
        // 把children 同时划分;
        if(!btr->children.empty()){
            auto child = btr->children.at(i);
            if(child!=nullptr) {
                child->father = right;
                right->children.push_back(child);
            }
        }
    }
    int v = btr->data[rightStart - 1];
    // 当前结点清理;以及清理多的子结点
    for(int i=rightStart-1;i<len;i++){
        btr->data.pop_back();

        if(!btr->children.empty()) {
            btr->children.pop_back();
        }
    }
    lpBNode pf = btr->father;
    int postion = getPositonForF(btr);
    if(pf == nullptr){
        auto node = new BTNode;
        node->data.push_back(v);
        node->children.push_back(btr);
        btr->father = node;
        node->children.push_back(right);
        right->father = node;
    } else {
        right->father = pf;
        pf->children.insert(pf->children.begin()+postion+1,right);
        pf->data.push_back(v);
        sort(pf->data.begin(),pf->data.end());
        if( pf->data.size() <= maxD) return;
        else BtSplit(pf);
    }
}

void insert(lpBNode& btr, int value){
    if(btr == nullptr){
        auto node = new BTNode;
        node->data.push_back(value);
        btr = node;
        return;
    }
    lpBNode bt = btr;
    int index;
    // 一直往下找到没有子结点的然后执行插入
    while (!bt->children.empty()){
        int curBtLen = bt->data.size();
        for(index=0;index<=curBtLen;++index){
            if(index!=curBtLen) {
                if (value < bt->data[index])break;
            } else break;
        }
        bt = bt->children.at(index);// 指针偏移
    }
    int btsize = bt->data.size();
    int i;
    for(i=0;i<=btsize;i++){
        if(i==btsize)break;
        if(value>bt->data[i])continue;
        else break;
    }
    bt->data.insert(bt->data.begin()+i,value);
    if( bt->data.size() <= maxD){
        return;
    } else{
        BtSplit(bt); // 从这个点开始向上迭代分割
    }
    lpBNode top = bt;
    while (bt->father!=nullptr){
        bt = bt->father;
    }
    btr = bt;
}

lpBNode builtBtree(std::vector<int>& nodes){
    lpBNode btn;
    btn = nullptr;
    for(auto node:nodes){
        insert(btn,node);
    }
    return btn;
}
int maxHeight(lpBNode btn){
    if(btn == nullptr){
        return 0;
    }
    // 根据我观察到的b树的特点,所有子树高度是一样的
    if(!btn->children.empty())return maxHeight(btn->children[0])+1;
    else return 1;

}
// 找到结点
lpBNode find(lpBNode btn,int node){
    if(btn == nullptr){
        return nullptr;
    }
    for (int i=0;i<(int)btn->data.size();i++) {
        if(btn->data.at(i) == node){
            return btn;
        }
    }
    if (!btn->children.empty())  {
        for(auto & i : btn->children){
            auto t = find(i,node);
            if(t != nullptr) return t;
        }
        // 都没返回 就返回nullptr;
        return nullptr;
    }
    else {
        return nullptr;
    }
}
lpBNode findPrecursor(lpBNode node,int v){
    int pdsize = node->data.size();
    int i;
    for(i=0;i<=pdsize;i++){
        if(i<pdsize) {
            if (node->data[i] == v)break;
        }
    }
    if(i==pdsize)return nullptr;
    // 前驱就是i,后继就是i+1
    if(node->children.empty())return node; // fixme: maybe fix up
    else {
        node = node->children[i];
        while (!node->children.empty()){
            node = *node->children.end();
        }
        return node;
    }
}
void remove_fix(lpBNode& targetNode){
    if(targetNode ->data.size() <= maxD && targetNode->data.size() >= minD){
        return;
    } else {
        lpBNode ph = targetNode->father;
        if(ph==nullptr)
            return;
        int pcsize = ph->children.size();// 子结点数量
        int i;
        for (i = 0; i < pcsize; i++) {
            if (ph->children[i] == targetNode) break; // 一定会有1个
        }
        // 从左到右来找兄弟结点
        // 左边 兄弟结点关键字足够借给该结点
        if (i - 1 >= 0 && ph->children[i - 1]->data.size() - 1 >= minD) {
            int dsize = ph->children[i - 1]->data.size();
            int lv = ph->children[i - 1]->data.at(dsize - 1);
            ph->children[i - 1]->data.pop_back();
            int pv = ph->data.at(i - 1);
            targetNode->data.insert(targetNode->data.begin(), pv);
            ph->data.at(i - 1) = lv;
        }
            // 右边 兄弟结点关键字足够借给该结点
        else if (i + 1 < pcsize && i + 1 < maxD && ph->children[i + 1]->data.size() - 1 >= minD) {
            int dsize = ph->children[i + 1]->data.size();
            int lv = ph->children[i + 1]->data.at(0);
            ph->children[i + 1]->data.erase(
                    ph->children[i + 1]->data.begin(),
                    ph->children[i + 1]->data.begin() + 1);
            int pv = ph->data.at(i);
            targetNode->data.push_back(pv);
            ph->data.at(i) = lv;
        } else if (i - 1 >= 0 && ph->children[i - 1]->data.size() - 1 < minD) {
            // 合并结点
            ph->children[i - 1]->data.insert(ph->children[i - 1]->data.end(),
                                             targetNode->data.begin(), targetNode->data.end());
            ph->children[i-1]->children.insert(ph->children[i-1]->children.end(),
                    targetNode->children.begin(),
                    targetNode->children.end());// 把该结点孩子弄到合并点那里去
            ph->children.erase(remove(ph->children.begin(), ph->children.end(), targetNode),
                               ph->children.end());// 删除这个结点
            int pv = ph->data.at(i - 1);
            ph->children[i - 1]->data.insert(ph->children[i - 1]->data.end(),
                                             pv);
            ph->data.erase(ph->data.begin() + i - 1);
            remove_fix(ph);
        } else if (i + 1 < pcsize && i + 1 <maxD && ph->children[i + 1]->data.size() - 1 < minD) {
            // 右子结点合并该结点所有剩余结点
            ph->children[i + 1]->data.insert(ph->children[i + 1]->data.begin(),
                                             targetNode->data.begin(), targetNode->data.end());

            ph->children[i+1]->children.insert(ph->children[i+1]->children.begin(),
                                               targetNode->children.begin(),
                                               targetNode->children.end());// 把该结点孩子弄到合并点那里去

            ph->children.erase(remove(ph->children.begin(), ph->children.end(), targetNode),
                               ph->children.end());// 删除这个结点
            int pv = ph->data.at(i);
            // 右子节点拿父亲的值
            ph->children[i]->data.insert(ph->children[i]->data.begin(),
                                             pv);
            // 父亲移除那个值
            ph->data.erase(ph->data.begin() + i);
            remove_fix(ph);
        }
    }
}
// 用的前驱方式
void remove(lpBNode& btn,int value){
    // 先找到要删除的关键结点
    lpBNode targetNode = find(btn, value);
    if(targetNode == nullptr){
        cout<<"cant't find target value"<<endl;
        return;
    }
    if(!targetNode->children.empty()){ // 确保有子结点
        // 前驱点一定是叶子结点
        lpBNode precursor = findPrecursor(targetNode, value);
        if(precursor==nullptr){
            cout<<"current value not matched"<<endl;
            return;
        }
        int i;
        for (i=0;i<(int)targetNode->data.size();i++) {
            if(targetNode->data.at(i) == value)break;
        }
//        auto lastNode = pre
        int cvalue = *(precursor->data.end()-1);//前驱点最大值
        targetNode->data.at(i) = cvalue;
        precursor->data.erase(std::remove(precursor->data.begin(),
                               precursor->data.end(),cvalue),
                               precursor->data.end());
        remove_fix(precursor);
        //非叶子结点清理
    }
    else {// 叶子结点清理
        targetNode->data.erase(std::remove(targetNode->data.begin(), // 删除固定值
                                            targetNode->data.end(), value),
                                targetNode->data.end());
        remove_fix(targetNode);
    }
    //  要找到前驱去代替那个值
    while (btn->father!=nullptr){
        btn = btn->father;
    }
}
void show(lpBNode btn){
    if (btn == nullptr){
        std::cout<<"B tree is null"<<std::endl;
        return;
    }
    int i = 0;
    int height = maxHeight(btn);
    std::vector<int> indents;
    std::vector<lpBNode > printContainer = std::vector<lpBNode>{btn};
    std::vector<lpBNode > storeContainer;
    std::cout<<"maxHeight: "<<height<<std::endl;
    for(i=height;i>0;i--){
        int indent = 4*i + i;
        std::string print;
        print.assign(indent,' ');
        for(auto btr:printContainer){
            if (btr != nullptr) {
                for(auto btv:btr->data){ //对于一个结点的所有data
                    print +=to_string(btv)+'|';
                }
                print.erase(print.end() - 1); // 去除掉最后一个'|'

                print.append(5, ' ');
                for(auto child:btr->children) {
                    storeContainer.push_back(child);
                }
            } else {
                print += "     ";
                print.append(5,' ');
                // FIXME: 这里需要修改
            }
        }
        printContainer.erase(printContainer.begin(),printContainer.end());
        printContainer.swap(storeContainer);

        print.erase(print.end()-5);
        print.append(indent,' ');
        std::cout<<print<<std::endl;
    }
}

int main(){
    vector<int> sv = vector<int>{16,36,19,31,20,30,10,15,25,40,22,18,29,90,55,61};
    cout<< "sv 元素尺寸数目:" << sv.size()<<endl;
    lpBNode btree = builtBtree(sv);
    remove(btree,40);
    remove(btree,36);
    remove(btree,15);
    remove(btree,22);
    remove(btree,25);
    remove(btree,61);
    show(btree);
    return 0;
}

照例给出可视化网站观察效果:
https://www.cs.usfca.edu/~galles/visualization/BTree.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!