B树的实现

来源:互联网 发布:淘宝网的鞋货源哪里找 编辑:程序博客网 时间:2024/06/05 20:49

二叉搜索树我们已经了解了很多种形式了,有AVLTree、红黑树等,不过,这都是在二叉搜索树的基础上加了些东西(平衡因子、颜色),其本质还是二叉搜索树。现在我们遇到一个问题:要是要查找的数据量很大的时候,还用这个,那么树的深度将会很深,那么要怎么解决呢?

要保证数据量不变的情况下,将树的深度降低。这就是B树了,今天我们就来了解一下B树的原理,以及实现过程。

B-树的定义(数据结构书上就是这么写的,不要读作:B减树):它是一种平衡的多路查找树,在文件系统中很有用。

B-树的性质:(假设树为M叉树)

1、若根结点不是叶子结点,那么至少有两棵树

2、每一个非根节点都至少有M/2-1(上取整)个关键字,至多有M-1个关键字,并以升序排序

3、每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子

4、key[i]与key[i+1]之间的孩子的关键字介于key[i]与key[i+1]之间

5、所有的叶子结点都在同一层

这里写图片描述

#include<iostream>using namespace std;template<class K, size_t M>struct BTreeNode{    //K _keys[M-1];     // 关键字的集合    //BTreeNode<K, M> _pSub[M];    // 孩子的集合    // 多给一个关键字:为简化分裂的逻辑(先将关键字放进去,再分裂)    K _keys[M];     // 关键字的集合    BTreeNode<K, M>* _pSub[M + 1];    // 孩子的集合    BTreeNode<K, M>* _pParent;    size_t _size; // 有效关键字的个数    BTreeNode()        : _size(0)        , _pParent(NULL)    {        for (size_t idx = 0; idx < M + 1; ++idx)            _pSub[idx] = NULL;    }};template<class K, size_t M>class BTree{    typedef BTreeNode<K, M> Node;public:    BTree()        : _pRoot(NULL)    {}    //查找,返回节点以及key在结点中的位置下标    pair<Node*, int> Find(const K& key)    {        Node* pCur = _pRoot;        Node* parent = NULL;        while (pCur)        {            size_t i = 0;            while (i < pCur->_size)            {                if (key < pCur->_keys[i])                    break;                else if (key>pCur->_keys[i])                    i++;                else                    return make_pair(pCur, i);            }            parent = pCur;//注意!            pCur = pCur->_pSub[i];        }        return make_pair(parent, -1);    }    bool Insert(const K& key)    {        //树空,直接插入        if (NULL == _pRoot)        {            _pRoot = new Node;            _pRoot->_keys[0] = key;            _pRoot->_size = 1;            return true;        }        //寻找插入位置        pair<Node*, int> pos = Find(key);        if (pos.second >= 0)//要插入的key存在            return false;        Node* pCur = pos.first;        Node* pSub = NULL;//指关键字右边的孩子,随着关键字的搬移,也要搬移        K k = key;//向上更新的时候不会改变原来key的值        while (true)        {            _InsertKey(pCur, k, pSub);            //看是否满足B-Tree的性质,不满足,则开始分裂            if (pCur->_size < M)                return true;            //不满足,那么_size只会是M            size_t mid = M >> 1;            //创建一个新的节点,将mid以后的,都放在新节点中            Node* newNode = new Node;            size_t i = mid + 1;//将i ,提出来,增加它的生命周期(for后面还要用)            for (; i < M; i++)            {                newNode->_keys[newNode->_size] = pCur->_keys[i];                newNode->_pSub[newNode->_size] = pCur->_pSub[i];                Node* sub = newNode->_pSub[newNode->_size];                if (sub)                    sub->_pParent = newNode;                ++(newNode->_size);            }            //处理最右边的一个孩子            newNode->_pSub[newNode->_size] = pCur->_pSub[i];            Node* sub = newNode->_pSub[newNode->_size];            if (sub)                sub->_pParent = newNode;            pCur->_size = pCur->_size - newNode->_size - 1;//不要忘记pCur的size已经改变了            //将mid放入根结点            if (_pRoot == pCur)//根结点就是pCur            {                _pRoot = new Node;                _pRoot->_keys[0] = pCur->_keys[mid];                _pRoot->_pSub[0] = pCur;                _pRoot->_pSub[1] = newNode;                //更新双亲                pCur->_pParent = _pRoot;                newNode->_pParent = _pRoot;                _pRoot->_size = 1;                return true;            }            else            {                //向上更新                k = pCur->_keys[mid];                pCur = pCur->_pParent;                pSub = newNode;            }        }           }    //中序遍历    void InOrder()    {        cout << " InOrder:";        _InOrder(_pRoot);        cout << endl;    }private:    void _InOrder(Node* pRoot)    {        if (pRoot)        {            for (int i = 0; i < pRoot->_size; i++)            {                _InOrder(pRoot->_pSub[i]);                cout << pRoot->_keys[i] << " ";            }            _InOrder(pRoot->_pSub[pRoot->_size]);        }    }    void _InsertKey(Node* pCur, const K& key, Node* pSub)    {        int end = pCur->_size - 1;        while (end >= 0)        {            //从后往前,如果,要插入的key小于当前的key,那么将这个关键字(和孩子)向后搬移            if (key < pCur->_keys[end])            {                pCur->_keys[end + 1] = pCur->_keys[end];   //搬key                pCur->_pSub[end + 2] = pCur->_pSub[end + 1];   //搬sub            }            else                break;            end--;        }        pCur->_keys[end + 1] = key;        pCur->_pSub[end + 2] = pSub;//不要忘了孩子        if (pSub)            pSub->_pParent = pCur;        pCur->_size++;    }private:    Node* _pRoot;};void TestBTree(){    BTree<int, 3> t;    t.Insert(10);    t.Insert(30);    t.Insert(20);    t.Insert(40);    t.Insert(50);    t.Insert(38);    t.Insert(35);    t.InOrder();}int main(){    TestBTree();    return 0;}

下面是这个插入 数据的分裂过程,可以参考一下

这里写图片描述

这里写图片描述

原创粉丝点击