B树代码

#include "xulity.h"
#include <functional>
#include <tuple>
#include <cassert>

enum class FindEnum
{
    FIND_FAIL = 0,
    FIND_SUCE = 1,
};

template<typename Ty>
struct BTreeNode
{
    typedef BTreeNode<Ty>* node_ptr;
    typedef uint32_t size_type;

    node_ptr parent;         //父指针
    node_ptr *container;     //子节点列表
    Ty *keylist;             //关键字列表
    size_type keynum;          //个数
    bool isleaf;             //是否是叶子节点(true),不是false
};

template<typename Ty, typename Alloc = AllocNode>
class BTree
{
    typedef BTreeNode<Ty> MyBase;
    typedef typename MyBase::node_ptr node_ptr;
    typedef MyBase& node_ref;
    typedef const node_ptr const_node_ptr;
    typedef const node_ref const_node_ref;
    typedef typename MyBase::size_type size_type;
    typedef std::function<int(const Ty&, const Ty&)> compare;
    typedef std::tuple<FindEnum, node_ptr, size_type, size_type> find_result;
public:
    BTree(size_type ChildNum, compare func)
        :MyHead(nullptr), MyChildNum(ChildNum), MyCompare(func)
    {}

    ~BTree()
    {
        if (MyHead)
        {
            clear(MyHead);
        }
    }
    void insert(const Ty& Val)
    {
        insert(MyHead, Val);
    }

    void clear()
    {
        clear(MyHead);
    }

    node_ptr& find(const Ty& data) const
    {
        return find(MyHead, data).get<1>();
    }

    void eraser(const Ty& data)
    {
        eraser(MyHead, data);
    }
private:
    size_type get_max_key() const
    {
        return MyChildNum - 1;
    }

    size_type get_max_child() const
    {
        return MyChildNum;
    }

    size_type get_mid_num() const
    {
        return CEILING_DIV_2(MyChildNum);
    }
    //需把要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的关键字合并成一个结点, 
    //即在删除关键字后,该结点中剩余的关键字加指针,加上双亲结点中的关键字Ki一起,
    //合并到Ai(是双亲结点指向该删除关键字结点的左(右)兄弟结点的指针)所指的兄弟结点中去。
    void mergy_node(node_ptr left, node_ptr right, size_type key_index, size_type child_index, bool mergy_format)
    {
        size_type i = 0,j = 0;
        auto parent = left->parent;

        if (mergy_format)
        {
            //将左节点keyIndex后的所有节点往前移//删除key_index指向关键字
            for (i = key_index; i < left->keynum - 1; ++i)
            {
                left->keylist[i] = left->keylist[i + 1];
                left->container[i + 1] = left->container[i + 2];
            }
            //加入父节点元素
            left->keylist[i] = parent->keylist[child_index];
            //加入把右节点的所有元素
            for (j = 0, i = left->keynum; j < right->keynum; j++,i++)
            {
                left->keylist[i] = right->keylist[j];
                left->container[i] = right->container[j];
                if (right->container[j])
                    right->container[j]->parent = left;//重新指定父亲节点
            }
            left->keylist[i] = right->keylist[j];
            if (right->container[j])
            {
                right->container[j]->parent = left;
            }
        } 
        else
        {
            //加入父节点元素
            right->keylist[right->keynum] = parent->keylist[child_index];
            //加入把右节点除被删除元素外的所有元素
            for (j = 0, i = left->keynum; j < key_index; j++, i++)
            {
                left->keylist[i] = right->keylist[j];
                left->container[i] = right->container[j];
                if (right->container[j])
                    right->container[j]->parent = left;//重新指定父亲节点
            }
            left->keylist[i] = right->keylist[j];
            if (right->container[j])
            {
                right->container[j]->parent = left;
            }
            for (j = key_index + 1; i < right->keynum; ++j,++i)
            {
                left->keylist[i] = right->keylist[j];
                left->container[i + 1] = right->container[j + 1];//???
                if (right->container[j + 1])
                    right->container[j + 1]->parent = left;//重新指定父亲节点
            }
        }
        left->keynum += right->keynum;
        remove_node(right);//释放右节点
        //求parent在其父节点的childIndex
        auto pp = parent->parent;
        size_type pp_index = 0;
        if (!parent)
            pp_index = -1;
        else
        {
            for (i = 0; i < pp->keynum + 1; ++i)
            {
                if (parent == pp->parent)
                {
                    pp_index = i;
                    break;
                }
            }
        }
        //删除父节点parent的key[leftIndex]
        eraser_node(parent, child_index, pp_index);
    }

    void eraser_node(node_ptr node, size_type key_index, size_type child_index)
    {
        size_type index = 0;
        //如果node中关键字个数大于ceiling(M/2)-1或者node为根节点,直接删除元素
        if (node->keynum >= get_mid_num() || nullptr == node->parent)
        {
            for (; index < node->keynum - 1; ++index)
            {
                node->keylist[index] = node->keylist[index + 1];
                node->container[index + 1] = node->container[index + 2];//???
            }
            node->keylist[node->keynum - 1] = Ty();
            node->container[node->keynum] = nullptr;
            node->keynum--;
            //如果为根节点,且没有任何元素,则释放节点,并指定树根为NULL!
            if (0 == node->keynum && nullptr == node->parent)
            {
                MyHead = node->container[0];//
                if (node->container[0])
                {
                    node->container[0]->parent = nullptr;
                }
                remove_node(node);
            }
        } 
        else//被删关键字所在结点的关键字个数n等于ceil(m/2)-1.不满足B树条件
        {
            /*如果其左右兄弟结点中有“多余”的关键字, 即与该结点相邻的右(左)兄弟结点中的关键字数目大于ceil(m / 2) - 1。
            则可将右(左)兄弟结点中最小(大)关键字上移至双亲结点。
                而将双亲结点中小(大)于该上移关键字的关键字下移至被删关键字所在结点中。*/
            auto parent = node->parent;
            auto left = parent->container[child_index - 1];
            auto right = parent->container[child_index + 1];
            size_type left_size = nullptr != left ? left->keynum : 0;
            size_type right_size = nullptr != right ? right->keynum : 0;
            size_type max_size = Max(left_size, right_size);
            if (max_size > get_mid_num())//右左兄弟结点中有“多余”的关键字
            {
                //则可将右(左)兄弟结点中最小(大)关键字上移至双亲结点。
                //而将双亲结点中小(大)于该上移关键字的关键字下移至被删关键字所在结点中
                //保持Ki<Ki+1
                if (left && left_size == max_size)////左兄弟关键字个数最多
                {
                    auto left_max_key = left->keylist[left->keynum - 1];//左节点最大
                    auto parent_key = parent->keylist[child_index - 1];////父节点关键字
                    auto left_max_child = left->container[left->keynum];//左兄弟最大孩子

                    parent->keylist[child_index - 1] = left_max_key;
                    left->keylist[left->keynum - 1] = Ty();
                    left->container[left->keynum] = nullptr;
                    left->keynum--;
                    //将pkey插入node中,并删除data
                    for (index = key_index; index > 0; --index)
                    {
                        //node节点keyIndex前关键字往后移
                        node->keylist[index] = node->keylist[index - 1];
                        node->container[index + 1] = node->container[index];
                    }
                    node->container[1] = node->container[0];
                    node->container[0] = left_max_child;
                    node->keylist[0] = parent_key;
                    if (left_max_child)
                    {
                        left_max_child->parent = node;
                    }
                } 
                else//右兄弟关键字多
                {
                    auto right_min_key = right->keylist[0];//右节点最小
                    auto parent_key = parent->keylist[child_index];////父节点关键字
                    auto right_min_child = left->container[0];//右兄弟最大孩子

                    parent->keylist[child_index] = right_min_key;
                    for (index = 0; index < right->keynum - 1; ++index)
                    {
                        right->keylist[index] = right->keylist[index + 1];
                        right->container[index] = right->container[index + 1];
                    }
                    right->keylist[right->keynum - 1] = Ty();
                    right->container[right->keynum - 1] = right->container[right->keynum];
                    right->container[right->keynum] = nullptr;
                    right->keynum--;
                    //将pkey插入node中,并删除data
                    for (index = key_index; index > node->keynum - 1; ++index)
                    {
                        //node节点keyIndex后关键字往前移
                        node->keylist[index] = node->keylist[index + 1];
                        node->container[index + 1] = node->container[index + 2];//?
                    }
                    node->container[node->keynum] = right_min_child;
                    node->keylist[node->keynum - 1] = parent_key;
                    if (right_min_child)
                    {
                        right_min_child->parent = node;
                    }
                }
            } 
            else//如果左右兄弟结点中没有“多余”的关键字
            {
                if (left && max_size == left->keynum)
                {
                    mergy_node(left, node, child_index - 1, key_index, true);//??
                } 
                else if (right && max_size == right->keynum)
                {
                    mergy_node(node, right, child_index, key_index, false);
                }
                return;
            }
        }
    }

    /*
    树删除data过程:
    1.寻找data的节点位置
    2.如果data在叶子节点中
    删除节点的data()
    3.else如果data在中间节点中
    寻找比大的最小元素(必在叶子节点)
    交换两个关键字(使之转换为在叶子节点中删除)
    删除节点的data()

    删除节点的data()
    1.如果node中关键字个数大于ceiling(M/2)-1或者node为根节点,直接删除元素
    删除元素
    如果为node根节点,且没有任何元素
    释放节点,并指定树根为NULL
    2.else如果node中关键字个数等于ceiling(M/2)-1
    寻找关键字最多的node的左右兄弟节点
    如果左兄弟关键字多,并多于ceiling(M/2)-1
    右旋最大关键字和孩子
    else如果右兄弟关键字多,并多于ceiling(M/2)-1
    左旋最小关键字和孩子
    否则
    如果左兄弟不为空
    合并左兄弟节点和node(data所在的索引)
    else如果右兄弟不为空
    合并node和右兄弟节点(data坐在的索引)

    合并左右节点(要删除关键字的索引)
    将除了要删除的关键字外的左右节点所有关键字和父节点的左节点索引关键字(x)合并在左节点中。
    释放右节点
    删除父节点的x()

    */
    void eraser(node_ptr pRoot, const Ty& data)
    {
        auto res = find(MyHead, data);
        if (FindEnum::FIND_FAIL == std::get<0>(res))
        {
            return;
        }
        auto find_node = std::get<1>(res);
        auto find_key_index = std::get<2>(res);
        auto find_child_index = std::get<3>(res);
        //if 要删除的key节点是子叶节点,在叶子节点中删除
        if (find_node->isleaf)
        {
            eraser_node(find_node, find_key_index, find_child_index);
        }
        else//else 不是叶子节点,寻找比node->key[keyIndex]大的最小元素,替换node->key[keyIndex]位置(转换成叶子节点)
        {
            auto temp = find_node->container[find_child_index + 1];
            size_type index = find_key_index + 1;
            while (temp->isleaf)
            {
                temp = temp->container[0];//最小的儿子
                index = 0;
            }
            find_node->keylist[find_key_index] = temp->keylist[0];
            eraser_node(temp, 0, index);
        }
    }
    //函数目的:在B树中递归的搜索给定的数据,如果存在则返回数据所在节点和在节点中的位置,不存在返回NULL
    //接收参数:   *Root - 树的根节点指针
    //             data - 要搜索的数据
    //返回参数:节点指针,数据所在节点,节点中的位置
    find_result& find(node_ptr pRoot, const Ty& data) const
    {
        auto node = pRoot;
        size_type i = 0, child_index = 0;
        while (node)
        {
            {
                int low = 0, high = node->keynum, mid = 0;
                while (low <= high)
                {
                    mid = (low + high) / 2;
                    if (MyCompare(node->keylist[mid], data) < 0)
                        high = mid - 1;
                    else if (MyCompare(node->keylist[mid], data) > 0)
                        low = mid + 1;
                    else
                    {
                        low = mid;
                        break;
                    }
                }
                i = low;
            }
            //数据找到,返回结果
            if (i < node->keynum && MyCompare(node->keylist[i], data) == 0)
            {
                return std::make_tuple(FindEnum::FIND_SUCE, node->container[i], i, child_index);
            }
            node = node->container[i];
            child_index = i;
        }
        return std::make_tuple(FindEnum::FIND_FAIL, node->container[i], i, child_index);
    }

    void clear(node_ptr& pRoot)
    {
        NULL_RETURN_VOID(pRoot);
        for (size_type i = 0; i < pRoot->keynum + 1; ++i)
        {
            clear(pRoot->container[i]);
        }
        remove_node(pRoot);
    }

    void remove_node(node_ptr& node)
    {
        NULL_RETURN_VOID(node);
        this->MyAlloc.Free(node->keylist);
        this->MyAlloc.Free(node->container);
        this->MyAlloc.Free(node);
        node = nullptr;
    }

    node_ptr buy_node(size_type num)
    {
        auto temp = (node_ptr)this->MyAlloc.Alloc(sizeof(MyBase));
        memset(temp, 0, sizeof(MyBase));
        temp->keylist = (Ty *)this->MyAlloc.Alloc(sizeof(Ty) * (num - 1));
        memset(temp->keylist, 0, sizeof(Ty) * (num - 1));
        temp->container = (node_ptr*)this->MyAlloc.Alloc(sizeof(node_ptr) * num);
        memset(temp->container, 0, sizeof(node_ptr) * num);
        return temp;
    }

    node_ptr buy_node(const Ty& data, size_type num)
    {
        auto temp = (node_ptr)this->MyAlloc.Alloc(sizeof(MyBase));
        assert(nullptr != temp);
        memset(temp, 0, sizeof(MyBase));
        temp->keylist = (Ty *)this->MyAlloc.Alloc(sizeof(Ty) * (num - 1));
        memset(temp->keylist, 0, sizeof(Ty) * (num - 1));
        temp->container = (node_ptr*)this->MyAlloc.Alloc(sizeof(node_ptr) * num);
        memset(temp->container, 0, sizeof(node_ptr) * num);

        temp->keylist[0] = data;
        temp->keynum = 1;
        temp->isleaf = true;
        return temp;
    }

    void insert_node(node_ptr pRoot, const Ty& data)
    {
        size_type pos = 0;
        //在叶子节点直接插入元素
        while (pos < pRoot->keynum && MyCompare(pRoot->keylist[pos], data) < 0)
            ++pos;
        //存在该元素
        if (MyCompare(pRoot->keylist[pos], data) == 0)
        {
            return;
        }
        for (auto i = pRoot->keynum; i > 0; i--)
            pRoot->keylist[i] = pRoot->keylist[i - 1];
        pRoot->keylist[pos] = data;
        pRoot->keynum++;
    }

    /*
    插入data过程:
    1.如果树根为空
    插入元素
    2.如果树根不空
    找到第一个比data稍小的最大叶子节点
    如果该叶子节点关键字个数少于M-1
    在该叶子节点中插入该元素
    否则
    分裂该叶子节点(data,NULL,NULL)

    分裂节点过程(插入元素,插入元素的左孩子,插入元素的右孩子):
    插入一个元素到满的节点中,并将改节点分为[0, celing(M/2) - 1]和[celing(M/2) + 1, M -1]左右两节点
    newData = key[celing(M/2)];
    如果分裂节点的父节点为空
    处理根节点
    如果分裂节点的父节点未满
    插入newData
    如果分裂节点的父节点已满
    分裂该父节点(newData, 左, 右)


    */
    void insert(node_ptr& pRoot, const Ty& Val)
    {
        if (nullptr == pRoot)
        {
            pRoot = buy_node(Val, MyChildNum);
            pRoot->isleaf = false;
            return ;
        }
        else
        {
            node_ptr node = pRoot;
            size_type position = 0;
            assert(nullptr != node);
            while (node->isleaf)//找到第一个比data稍小的最大叶子节点
            {
                position = 0;
                //找到第一个比Val稍小的最大叶子节点
                while (position < pRoot->keynum && MyCompare(pRoot->keylist[position], Val) < 0)
                    position++;
                //存在该元素
                if (MyCompare(pRoot->keylist[position], Val) == 0)
                {
                    return ;
                }
                node = node->container[position];
            }
            //如果该叶子节点未满
            if (pRoot->keynum < get_max_key())
                insert_node(pRoot, Val);
            else
                slipt_node(node, Val, nullptr, nullptr);
        }
    }

    //node分裂灰构造2个节点(left,right)
    //同时调整父节点(3种情况)1.父节点为空。2.父节点没满。3.父节点已满再次分裂
    void slipt_node(node_ptr node, const Ty& data, node_ptr left, node_ptr right)
    {
        size_type index = 0;
        size_type i = 0;
        size_type j = 0;
        size_type mid = CEILING_DIV_2(MyChildNum);
///////////寻找data数据是否在node节点keylist里////////////////////////////////////////////////////
        //寻找data在node->key中的索引,使得node->key[i]>=data
        for (; i < node->keynum && MyCompare(node->keylist[i], data) < 0;)
        {
            ++i;
        }
        index = i;
        //存在该元素
        if (MyCompare(node->keylist[i], data) == 0)
            return;
/////////用2个临时内存存储原来数据////////////////////////////////////////////////////////////////////
        //key和child都比node中的key和child多一个元素,使之成为一个类似于M+1阶B-树的节点
        auto keylist = (Ty *)this->MyAlloc.Alloc(sizeof(Ty) * (get_max_key() + 1));
        auto childlist = (node_ptr*)this->MyAlloc.Alloc(sizeof(node_ptr) * (get_max_child() + 1));
        for (i = 0; i < index; ++i)
        {
            keylist[i] = node->keylist[i];
            childlist[i] = node->container[i];
        }
        keylist[index] = data;
        childlist[i] = left;//
        childlist[i + 1] = right;//

        for (i = index; i < node->keynum; ++i)
        {
            keylist[i + 1] = node->keylist[i];
            childlist[i + 2] = node->container[i + 1];
        }
///////////////将原来的一个节点node分为左右两节点node和new_node////////////////
        auto new_data = keylist[mid];
        auto new_node = buy_node(data, MyChildNum);
        new_node->keynum = 0;
        node->keynum = 0;
        for (i = 0; i < mid; ++i)
        {
            node->keylist[i] = keylist[i];
            node->container[i] = childlist[i];
            node->keynum++;
        }
        node->container[i] = childlist[i];

        for (j = 0, i = mid + 1; i < get_max_child() + 1; j++, i++)
        {
            new_node->keylist[j] = keylist[i];
            new_node->keynum++;
            if (nullptr != childlist[i])
            {
                childlist[i]->parent = new_node;//重新指定其分裂到右节点中的孩子的父节点
                new_node->container[j] = childlist[i];
            }
        }

        new_node->isleaf = node->isleaf;//node和new_node同级
        new_node->parent = node->parent;
        this->MyAlloc.Free(keylist);
        this->MyAlloc.Free(childlist);
////////////////////	//处理父节点///////////////////////////////
        auto parent = node->parent;
        if (nullptr == parent)
        {
            parent = buy_node(MyChildNum);
            parent->keylist[0] = new_data;
            parent->container[0] = node;
            parent->container[1] = new_node;
            parent->keynum++;
            parent->parent = nullptr;
            parent->isleaf = false;
            MyHead = parent;
            node->parent = parent;
            new_node->parent = parent;
        } 
        else if (parent->keynum < MyChildNum)
        {
            i = 0;
            while (i < parent->keynum && MyCompare(parent->keylist[i], new_data) < 0)
            {
                ++i;
            }
            for (j = parent->keynum; j > i; --j)
            {
                parent->keylist[j] = parent->keylist[j - 1];
                parent->container[j - 1] = parent->container[j];
            }
            parent->keylist[i] = new_data;
            parent->container[i] = node;
            parent->container[i + 1] = new_node;
            parent->keynum++;
        }
        else
            slipt_node(parent, new_data, node, new_node);
    }
private:
    Alloc MyAlloc;
    node_ptr MyHead;
    size_type MyChildNum;   //x阶
    Compare MyCompare;   //谓词函数
};

以下代码还有问题,主要是把思路写下来。看什么时候有时间修改

1.增加断言判断,Debug下打印

2.引入STL管理内存,支持泛型

    原文作者:B树
    原文地址: https://blog.csdn.net/zhangbeiiii/article/details/51379652
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注