B树的基本定义与B树的插入

B树
1.B树的概念:
B树是平衡的多叉树,一个节点有多于两个(不能小于)结点的平衡多叉树。
缺点:浪费空间
2.B树的基本性质:
<1>.根节点至少有两个孩子
<2>.每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子。
<3>.每个非根节点至少有M/2-1(上取整)个关键字,至多有M-1个关键字。并以升序排列。
<4>.key[i]和key[i+1]之间的孩子节点的值介于key[i]和key[i+1]之间。
<5>.所有的叶子节点都在同一层。
3.B树的插入:
这里写图片描述
这里写图片描述
这里写图片描述
<4>.B树的节点:
因为B树中有关键字与孩子,如上图所示因为其包含key[M-1]与sub[M]。则将其kv数组给成pair型。即pair

template<class K,class V,size_t M>//M 为节点个数
struct BTreeNode
{
    //kvs和subs多一个空间是为了插入以后在分裂
    //简化分裂的逻辑
    pair<K, V>_kvs[M];         //kvs
    BTreeNode<K, V, M>* _subs[M + 1];//孩子节点
    BTreeNode<K, V, M>* _parent;
    size_t _size;//kvs的数量
    BTreeNode()
        :_parent(NULL)
        , _size(0)
    {
        for (size_t i = 0; i < M+1; i++)
        {
            _subs[i] = NULL;
        }
    }
};

<6>. B树的构造:
函数1:Find函数:
查找树中是否有和key值相同的数值。
查找返回值是一个pair,如果找到了结点,返回结点cur和此结点的位置,没有找到则返回父节点和-1。
查找的具体过程就是遍历结点构成的数组即可,需要注意的是边界条件,同时在遍历过程中看在树的左边和后面。

pair<pNode, int>Find(const K&key)//查找函数
    {
        pNode cur = _root;
        pNode parent = NULL;
        while (cur)
        {
            size_t i = 0;
            while (i < cur->_size)
            {
                if (cur->_kvs[i].first == key)//若找到则返回节点与其位置
                {
                    return make_pair(cur, i);
                }
                else if (cur->_kvs[i].first>key)//若key值小于cur->_kvs[i].first,则在其左子树中
                {
                    break;
                }
                else   //在其右子树中
                {
                    ++i;
                }
            }
            parent = cur;
            cur = cur->_subs[i];
        }
        //若找不到则返回-1;
        return make_pair(parent, -1);
    }

函数2:Insert函数:
插入方法:
①如果root为空,则构造结点直接插入,将size值更新为1。
②当root不为空时,调用Find函数。由于返回值是一个pair,所以判断返回值的第二个参数,如果存在,则参数为大于等于0,插入不成功返回false,否则进行插入
④没有找到的时候返回pair的第一个参数是插入节点的父亲结点,构造节点调用insertkv(此处逻辑复杂,单独写出来)进行插入。
⑤判断插入后结点的size值,如果小于M则直接插入成功,反之需要进行分裂。
这里写图片描述

            size_t j = 0;//将右半区间拷走
                for (size_t i = mid + 1; i < cur->_size; ++i)
                {
                    brother->_kvs[j] = cur->_kvs[i];
                    brother->_subs[j] = cur->_subs[i];
                    if (brother->_subs[j])
                    {
                        brother->_subs[j]->_parent = brother;
                    }
                    ++j;
                    brother->_size++;//增加节点
                }
                //拷完后还剩一个右孩子
                brother->_subs[j] = cur->_subs[cur->_size];//将最后一个拷给brother。
                cur->_size = cur->_size - brother->_size -1;

完整的插入代码如下:

#include <iostream>
#include <string>
using namespace std;
template<class K,class V,size_t M>//M 为节点个数
struct BTreeNode
{
    //kvs和subs多一个空间是为了插入以后在分裂
    //简化分裂的逻辑
    pair<K, V>_kvs[M];         //kvs
    BTreeNode<K, V, M>* _subs[M + 1];//孩子节点
    BTreeNode<K, V, M>* _parent;
    size_t _size;//kvs的数量
    BTreeNode()
        :_parent(NULL)
        , _size(0)
    {
        for (size_t i = 0; i < M+1; i++)
        {
            _subs[i] = NULL;
        }
    }
};
template<class K, class V, size_t M>//M 为节点个数
class BTree
{
    typedef BTreeNode<K, V, M> Node;
    typedef Node* pNode;
public:
    BTree()
        :_root(NULL)
    {}
    pair<pNode, int>Find(const K&key)//查找函数
    {
        pNode cur = _root;
        pNode parent = NULL;
        while (cur)
        {
            size_t i = 0;
            while (i < cur->_size)
            {
                if (cur->_kvs[i].first == key)//若找到则返回节点与其位置
                {
                    return make_pair(cur, i);
                }
                else if (cur->_kvs[i].first>key)//若key值小于cur->_kvs[i].first,则在其左子树中
                {
                    break;
                }
                else   //在其右子树中
                {
                    ++i;
                }
            }
            parent = cur;
            cur = cur->_subs[i];
        }
        //若找不到则返回-1;
        return make_pair(parent, -1);
    }
    bool Insert(const pair<K, V>&kv)//插入函数
    {
        //1.若为空树
        if (_root == NULL)
        {
            _root = new Node;
            _root->_kvs[0] = kv;
            _root->_size = 1;
            return true;
        }
        //2.
        pair<pNode, int>ret = Find(kv.first);
        if (ret.second != -1)
        {
            return false;//说明key在节点中`
        }
        pNode cur = ret.first;
        pair<K, V>newkv = kv;
        pNode sub = NULL;
        while (1)//不断地向上分裂
        {
            Insertkv(cur, newkv, sub);
            if (cur->_size < M)
            {
                return true;
            }
            else
            {
                //走到此,表示节点已满,需要进行分裂
                pNode brother = new Node;
                //将cur节点的右半区间分裂给兄弟节点
                size_t mid = (M / 2);
                size_t j = 0;//将右半区间拷走
                for (size_t i = mid + 1; i < cur->_size; ++i)
                {
                    brother->_kvs[j] = cur->_kvs[i];
                    brother->_subs[j] = cur->_subs[i];
                    if (brother->_subs[j])
                    {
                        brother->_subs[j]->_parent = brother;
                    }
                    ++j;
                    brother->_size++;//增加节点
                }
                //拷完后还剩一个右孩子
                brother->_subs[j] = cur->_subs[cur->_size];//将最后一个拷给brother。
                cur->_size = cur->_size - brother->_size -1;
                if (cur == _root)
                {
                    _root = new Node;
                    _root->_kvs[0] = cur->_kvs[mid];
                    _root->_subs[0] = cur;
                    _root->_subs[1] = brother;
                    _root->_size = 1;
                    cur->_parent = _root;
                    brother->_parent = _root;
                    return true;
                }
                else//有父亲时
                {
                    newkv = cur->_kvs[mid];
                    sub = brother;
                    cur = cur->_parent;
                }
            }
        }
    }
    void Insertkv(pNode cur, const pair<K, V>&kv, pNode sub)//sub为一个孩子
    {
        int end = cur->_size - 1;
        while (end >= 0)
        {
            if (cur->_kvs[end].first < kv.first)//若其值大于cur->_kvs[end].first,则需放在end+1的位置
            {
                break;
            }
            else
            {
                cur->_kvs[end + 1] = cur->_kvs[end];
                cur->_subs[end + 2] = cur->_subs[end + 1];
                --end;
            }
        }
        cur->_kvs[end + 1] = kv;
        cur->_subs[end + 2] = sub;//挪走右孩子
        if (sub)
        {
            sub->_parent = cur;
        }
        cur->_size++;
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }

    void _InOrder(Node* root)
    {
        if (root == NULL)
            return;
        Node* cur = root;
        size_t i = 0;
        for (; i < cur->_size; ++i)//cur->_size:节点的个数即为上边key的个数
        {
            _InOrder(cur->_subs[i]);//左孩子
            cout << cur->_kvs[i].first << " " << cur->_kvs[i].second << endl;
        }
        _InOrder(cur->_subs[i]);
    }
private:
    Node* _root;
};
void TestBTree()
{
    BTree<int, int, 3> t;
    int a[] = { 53, 75, 139, 49, 145, 36, 101 };
    for (size_t i = 0; i < (sizeof(a) / sizeof(a[0])); ++i)
    {
        t.Insert(make_pair(a[i], i));
    }
    t.InOrder();
}
int main()
{
    TestBTree();
    system("pause");
    return 0;
}

这里写图片描述
图可能有点丑,多多包涵。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值