数据结构-b树实现

b树的实现

main.cpp

#include <iostream>
#include <string>
#include "main.h"

/* #define MYHEAP_TEST */
/* #define HEAP_SORT_TEST */
/* #define INDEX_MIN_PRIORITY_TEST */
/* #define RED_BLACK_TREE_TEST */
#define BTREE_TEST

using std::string;


int main()
{

#ifdef BTREE_TEST
    //B树的测试
    //注意,本文件只给出了阶数为奇数时的b树代码实现
    bTree<int> obj(5);   
    obj.insertTree(70, 70);
    obj.insertTree(40, 40);
    obj.insertTree(80, 80);
    obj.insertTree(30, 30);
    obj.insertTree(50, 50);
    obj.insertTree(100, 100);
    obj.insertTree(110, 110);
    obj.insertTree(300, 300);
    obj.insertTree(120, 120);
    obj.insertTree(60, 60);
    obj.insertTree(130, 130);
    obj.insertTree(20, 20);
    obj.insertTree(310, 310);
    obj.insertTree(10, 10);
    obj.insertTree(11, 11);
    obj.insertTree(90, 90);
    obj.insertTree(91, 91);

    obj.insertTree(41, 41);
    obj.insertTree(42, 42);
    obj.insertTree(43, 43);
    
    obj.insertTree(44, 44);
    obj.insertTree(45, 45);
    obj.insertTree(46, 46);

    obj.insertTree(12, 12);
    obj.insertTree(13, 13);
    obj.insertTree(14, 14);

    obj.insertTree(15, 15);
    obj.insertTree(16, 16);
    obj.insertTree(17, 17);

    obj.insertTree(31, 31);
    obj.insertTree(32, 32);
    obj.insertTree(33, 33);

    obj.insertTree(34, 34);
    obj.insertTree(35, 35);
    obj.insertTree(36, 36);

#endif
    return(0);
}

main.h

#ifndef __MAIN_H__
#define __MAIN_H__
#include <vector>

using std::vector;

#include "heap.h"
#include "heapSort.h"
#include "indexMinPriorityQueue.h"
#include "biTriTree.h"
#include "redBlackBT.h"
#include "bTree.h"

#endif

bTree.h

#ifndef __BTREE_H__
#define __BTREE_H__
#include "main.h"
    /*
    * B树是2-3树的扩展
    * B树的概念
    * 1. 每个节点最多M-1个key,并且以升序排列
    * 2. 每个节点最多有M个子节点(称为M阶B树)
    * 3. 根节点至少右两个子节点
    *
    * B树性质
    * 1. 实际应用中,M一般大于100,那么即使在数据量比较大时
    * 树仍然比较矮,更便于查询
    *
    * 备注
    * 本文件只给出了b树在阶数为奇数情况下的插入的实现
    * 阶数-1就是节点集中允许稳定存在键值对个数
    */

/*****存放键值对(键和值)*****/
template <typename T>
struct kv{
    int key; //键
    T val;   //值
};

/*
* 节点集类,一颗b树有很多层,每层有多个节点集,每个节点集
* 有多个键值对,且每个节点集都有一个孩子集(孩子集中每个
* 元素都是一个分支)
*/
template <typename T>
class BNodeSet{
public:
    BNodeSet(){ };
    BNodeSet(int m);
    int MNum;   //阶数,与B树的阶数相同
    int keyNum; //当前节点集的已有键值对的数量
    void insVK2NS(int k, T v);
    void insChild2NS(BNodeSet<T>* x, BNodeSet<T>* x1,
                     BNodeSet<T>* y);

    //用于存放每个节点集的孩子集,且定有keyNum+1个孩子集
    vector<BNodeSet<T>*> childSetP; 
    //键值对数组
    vector<kv<T>> kvArr; 
    
};

/***************************************************************
* Description : 节点集初始化
* Input: m为阶数,也就是m-1为节点集允许稳定存在的键值对数量
***************************************************************/
template <typename T>
BNodeSet<T>::BNodeSet(int m){
    MNum = m;
    keyNum = 0;
    //初始化节点集的键值,为节点集的键值对占位
    //故意多占一个键值对位,用于处理不稳定态节点集
    kvArr.resize(m);
    for(int i=0; i < m; i++){
        (kvArr[i]).key = -1;
        (kvArr[i]).val = T();
    }
    //初始化该节点集的孩子集的
    //故意多占一个孩子集,用于处理不稳定态的节点集
    childSetP.resize(m+1);
    for(int i=0; i < m+1; i++){
        childSetP[i] = new BNodeSet<T>;
        childSetP[i] = 0;
    }
}

//对某一节点集的键值对进行有序插入
/***************************************************************
* Description : insert val-key to node set,将待插入键值对插入到
*               keyVal向量中
* Input : 待插入键k,待插入值v
***************************************************************/
template <typename T>
void BNodeSet<T>::insVK2NS(int k, T v){
    queue<kv<T>> que; //用于中转,存储某一节点集所有的键值对
    bool flag = false;//表示已插入目标键值对
    kv<T> tmp;
    tmp.key = k;
    tmp.val = v;
    if(keyNum == 0){
        kvArr[0] = tmp;
        ++(keyNum);
        return;
    }
    //将带插入键值对暂放到队列中
    for(int i=0; i < keyNum; i++){
        //直接覆盖掉具有相同key的值
        if(k == (kvArr[i]).key){
            kvArr[i] = tmp; 
            return;
        }
        //待插入键值对比已有的最大键值对还大
        if(k > kvArr[keyNum-1].key){
            kvArr[keyNum] = tmp;
            keyNum++;
            return;
        }
        
        //待插入键值对大小的一般情况
        if((k < (kvArr[i]).key) && (flag == false)){
            flag = true;//只插入一次该带插入键值对
            que.push(tmp);
            que.push(kvArr[i]);
        }else{
            que.push(kvArr[i]);
        }
    }
    ++(keyNum); //增加了一个键值对(待插入键值对)
    
    //将队列que中的值弹出到kvArr中
    int i = 0;
    while(!que.empty()){
        kvArr[i] = que.front();    
        que.pop();
        i++;
    }
}
/***************************************************************
* Description : 找到x1在x的孩子集中的位置,并将y插入到x1所在位置
*               的后面
* Input : x-目标节点集,x1-目标孩子,y-插入到x1后面的孩子
***************************************************************/
template <typename T>
void BNodeSet<T>::insChild2NS(BNodeSet<T>* x,
                              BNodeSet<T>* x1, BNodeSet<T>* y){
    queue<BNodeSet<T>*> que;
    bool flag = false;
    //实现原理和insVK2NS类似
    for(int i=0; i < x->keyNum; i++){
        if((flag == false) && (x->childSetP[i] == x1)){
            flag = true;
            que.push(x->childSetP[i]);
            que.push(y);
        }else{
            que.push(x->childSetP[i]);
        }
    }
    int i = 0;
    while(!que.empty()){
        x->childSetP[i] = que.front();
        que.pop();
        i++;
    }
}

/*
* b树类
*/
template <typename T>
class bTree{
public:
    bTree(int m){
        M = m; 
        rootTree = 0;
    }
    void insertTree(int k, T val);
private:
    //将键值对插入到某树的某一个节点集中
    void insertTree(BNodeSet<T>* r,BNodeSet<T>* pre,
                            int k, T val);
    //判断节点集的键值对数量是否已超过阶数-1
    bool isOF(BNodeSet<T>* x); 
    //将满键值对数量的节点集进行分裂
    void split(BNodeSet<T>* r, BNodeSet<T>* pre);

    int M;  //阶数
    BNodeSet<T>* rootTree;//树的根节点
};

/***************************************************************
* Description : is over flowed,判断某一节点集中键值对的数量是否
*           溢出,比如阶数为5的b树,那么稳定状态的键值对数量是4,
*           暂时的非稳定状态是5。就是判断该节点集是否是稳定的
* Output: 溢出则返回true
***************************************************************/
template <typename T>
bool bTree<T>::isOF(BNodeSet<T>* x){
    return((x->keyNum) == (x->MNum));
}

template <typename T>
void bTree<T>::insertTree(int k, T val){
    insertTree(rootTree, 0, k, val);
}

/***************************************************************
* Description : 向某颗树中的某个节点集中插入一个键值对
* Input : dadSet-父节点集,sonSet-子节点集
*         k-待插入键,v-待插入值
***************************************************************/
template <typename T>
void bTree<T>::insertTree(BNodeSet<T>* sonSet,
                                  BNodeSet<T>* dadSet,
                                  int k, T v){
    if(rootTree == 0){
        rootTree = new BNodeSet<T>(M);
        rootTree->insVK2NS(k, v);
        return;
    }
    //到达叶子节点集
    if(sonSet->childSetP[0] == 0){
        sonSet->insVK2NS(k, v);
        if(sonSet->keyNum == sonSet->MNum){
            split(sonSet, dadSet);
        }
        return;
    }
    //选择接下来应该走哪个分支
    for(int i=0; i < sonSet->keyNum; i++){
        if(k == (sonSet->kvArr[i]).key){
            (sonSet->kvArr[i]).val = v;//直接覆盖原来的值
            return;
        }
        if(k < (sonSet->kvArr[i]).key){
            insertTree(sonSet->childSetP[i],sonSet,
                           k, v);
            break;
        }
        //选择最右边的那条分支
        if(i == (sonSet->keyNum - 1)){
            insertTree(sonSet->childSetP[sonSet->keyNum],
                       sonSet,k, v);
            break;
        }
    }
    
    //判断当前节点集是否需要进行分裂,节点集元素个数大于M-1
    if(((sonSet != 0) && isOF(sonSet)) || 
       ((dadSet == 0) && isOF(sonSet))){
        split(sonSet, dadSet); //进行分裂
    }    

    return;
}

/***************************************************************
* Description : 将满足键值对数溢出的节点集进行分裂
* Input : sonSet是待分裂的节点集,dadSet是分裂后中间位应当存储的
*         的位置,sonSet是dadSet的孩子
***************************************************************/
//返回分裂调整后的父节点
template <typename T>
void bTree<T>::split(BNodeSet<T>* sonSet,
                                 BNodeSet<T>* dadSet){
    //记录sonSet里面中间位置键值对的索引
    int midIndex = (M-1)/2;
    //再生成一个新的节点集,存放sonSet中后一半数量的键值对
    //将sonSet中后一半的孩子放入到newNodeSet中
    BNodeSet<T>* newNodeSet = new BNodeSet<T>(M);
    int j = 0;
    for(j=0; j < midIndex; j++){
        newNodeSet->kvArr[j].key = 
            sonSet->kvArr[midIndex+j+1].key;
        newNodeSet->kvArr[j].val = 
            sonSet->kvArr[midIndex+j+1].val;
        sonSet->kvArr[midIndex+j+1].key = -1;
        sonSet->kvArr[midIndex+j+1].val = T (); //清除后一半的键值对
        newNodeSet->childSetP[j] = 
            sonSet->childSetP[midIndex+j+1];
        sonSet->childSetP[midIndex+j+1] = 0; //清除后一半孩子集记录
        ++(newNodeSet->keyNum);
        --(sonSet->keyNum);
    }
    //将最后一个孩子复制给新的set的孩子集
    newNodeSet->childSetP[newNodeSet->keyNum] = 
            sonSet->childSetP[midIndex+j+1];
    sonSet->childSetP[midIndex+j+1] = 0; //清除后一半孩子集记录

    //如果此时只有一个节点,且该节点为根节点
    //那么就要再创建一层节点集
    if(dadSet == 0){
        dadSet = new BNodeSet<T>(sonSet->MNum);
        //将sonSet的中间位置的键值对放入父节点集中
        dadSet->insVK2NS(sonSet->kvArr[midIndex].key, 
                          sonSet->kvArr[midIndex].val);
        sonSet->kvArr[midIndex].key = -1;
        sonSet->kvArr[midIndex].val = T();
        --(sonSet->keyNum);
        //将新生成的和原来的节点集放到父节点集的孩子集中
        dadSet->childSetP[0] = sonSet;
        dadSet->childSetP[1] = newNodeSet;
        rootTree = dadSet;
    }else{
        //将sonSet的中间位置的键值对放入父节点集中
        dadSet->insVK2NS(sonSet->kvArr[midIndex].key, 
                          sonSet->kvArr[midIndex].val);
        sonSet->kvArr[midIndex].key = -1;
        sonSet->kvArr[midIndex].val = T();
        --(sonSet->keyNum);

        //将新生成的节点集放到父节点集的孩子集中
        dadSet->insChild2NS(dadSet, sonSet, newNodeSet);
        /* dadSet->childSetP[dadSet->keyNum] = newNodeSet; */
    }
}

#endif

```cpp

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值