【数据结构】B树的创建

B树也是一种搜索树,二叉搜索树、红黑树、都是动态查找树,典型的二叉搜索结构,查找的时间复杂度和树的高度相关O(log2N)。

这些二叉搜索结构有一个共同的缺陷:数据量大,树的高度太高,增大访问磁盘的次数,从而效率低下。

想要加速对数据的访问速度:
1.提高I/O的时间
2.降低树的高度——平衡多叉树

B树的定义
一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足以下性质:
1.根节点至少有两个孩子。
2.每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子。
3.每个非根节点至少有M/2-1(上取整)个关键字,至多有M-1个关键字,并且以升序排列
4.key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间。
5.所有的叶子节点都在同一层。

例:
插入{53,75,139,49,145,36,101};

M阶B树——M=3
3阶B树的插入过程:
《【数据结构】B树的创建》
《【数据结构】B树的创建》《【数据结构】B树的创建》《【数据结构】B树的创建》
代码实现过程:
1.首先设定结构体,实际只用到两个关键码,但是为了交换简单,我们设置三个关键码,孩子指针域始终比关键码多一个。
《【数据结构】B树的创建》
2.创建一个类将B树的函数操作封装起来。
3.B树的插入:
(1)根节点为空,开辟一个新节点newNode,将key的值赋给newNode,对size+1,最后使根指向newNode。
(2)根不为空,通过Find函数找到插入位置cur,通过_InsertKey()函数将key的值赋给cur,(cur->size)+1,判断cur->size与M的大小,如果cur->size < M则return true;
(3)如果cur->size=M,要将节点进行分裂,将中间的关键码向上提,new一个节点将最右边的关键码赋值给key[0]。
(4)每次改变节点要记得更改双亲的指向。

【BTree.h】

#pragma once
#include<iostream>
using namespace std;
#define M 3

template<class K>
struct BTreeNode
{
    K _keys[M];        //关键码
    BTreeNode<K>* _pSub[M + 1];  //孩子指针域
    BTreeNode<K>* _pParent;
    size_t size;
    BTreeNode()
        :_pParent(NULL)
        , size(0)
    {
        for (int i = 0; i < (M + 1); i++)
            _pSub[i] = NULL;
    }
};

template<class K>
class BTree
{
    typedef BTreeNode<K> Node;
    typedef Node* pNode;
public:
    BTree()
     :pRoot(NULL)
    {}
    bool BTree_Insert(K value)
    {
            if (pRoot == NULL)
            {
                pRoot = new Node;
                pRoot->_keys[0] = value;
                pRoot->size = 1;
                return true;
            }

            //节点的关键字满了就进行分裂
            pair<pNode,int> findNode = _Find(value);    
            if (findNode.second >= 0)     //找到相同关键码
                return false;

            //没找到相同节点,可以插入新节点
            pNode cur = findNode.first;
            K newkey = value; 
            pNode sub = NULL;

            //向cur插入newkey,sub
            while (1)
            {
                _InsertKey(cur, newkey, sub);   //插入一个孩子和一个关键字
                if (cur->size < M)
                    return true;
                else
                {
                    //需要分裂
                    pNode newNode = _splitblock(cur);
                    K midkey = cur->_keys[(cur->size) / 2];
                    //根节点分裂
                    cur->size = (cur->size) - (newNode->size + 1);
                    if (cur == pRoot)
                    {
                        pRoot = new Node;
                        pRoot->_keys[0] = midkey;
                        pRoot->size = 1;
                        pRoot->_pSub[0] = cur;
                        pRoot->_pSub[1] = newNode;
                        cur->_pParent = pRoot;
                        newNode->_pParent = pRoot;

                        return true;
                    }
                    else
                    {
                        sub = newNode;
                        newkey = midkey;
                        cur = cur->_pParent;
                    }
                }
            }
    }
    void InOrder()        //中序遍历
    {
        _InOrder(pRoot);
        cout << endl;
    }
private:
    pNode _splitblock(pNode cur)    //分裂函数
    {  
        pNode newNode = new Node;
        int mid = (cur->size) / 2;
        size_t j = 0;
        size_t i = mid + 1;

        for (; i < cur->size; i++)
        {
            newNode->_keys[j] = cur->_keys[i];
            newNode->_pSub[j] = cur->_pSub[i];
            if (newNode->_pSub[j] != NULL)
                newNode->_pSub[j]->_pParent = newNode;
            newNode->size++;
            j++;
        }


        //拷右孩子
        newNode->_pSub[j] = cur->_pSub[i];
        if (cur->_pSub[i] != NULL)
            newNode->_pSub[j]->_pParent = newNode;

        return newNode;
    }

    void _InsertKey(pNode cur,const K value,pNode sub)         //插入一个孩子和一个关键码
    {
        int end = cur->size - 1;
        while (end >= 0)
        {
            if (cur->_keys[end] > value)
            {
                cur->_keys[end + 1] = cur->_keys[end];
                cur->_pSub[end + 2] = cur->_pSub[end + 1];
                end--;
            }
            else
                break;
        }

        //当end<0 或 value > cur->keys[end]
        cur->_keys[end + 1] = value;
        cur->_pSub[end + 2] = sub;

        if (sub != NULL)
            sub->_pParent = cur;
        cur->size++;
    }

    pair<pNode,int> _Find(K value)
    {
        pNode ret = pRoot;
        pNode parent = NULL;
        while (ret)
        {
            size_t i = 0;
            while (i < ret->size)
            {
                //在当前位置的左树
                if (value < (ret->_keys[i]))
                    break;

                else if (value>(ret->_keys[i]))
                {
                    i++;
                }
                else                         //有相同关键码
                    return make_pair(ret, i);     
            }
            //在左树或没找到
            parent = ret;
            ret = ret->_pSub[i];
        }
        return make_pair(parent,-1);
    }

    void _InOrder(pNode _pRoot)     //中序遍历
    {
        if (_pRoot == NULL)
            return;

        pNode cur = _pRoot;
        size_t i = 0;
        for (i = 0; i < cur->size; i++)
        {
            _InOrder(cur->_pSub[i]);
            cout << cur->_keys[i] << " ";
        }
        _InOrder(cur->_pSub[i]);
    }

private:
    pNode pRoot;
};

void test()
{
    int arr[] = { 53, 75, 139, 49, 145, 36, 101 };
    int size = sizeof(arr) / sizeof(arr[0]);
    BTree<int> bt;
    for (int i = 0; i < size; i++)
    {
        bt.BTree_Insert(arr[i]);
    }
    bt.InOrder();
}

【test.cpp】

#include"BTree.h"
int main()
{
    test();
    system("pause");
    return 0;
}
    原文作者:B树
    原文地址: https://blog.csdn.net/lz201788/article/details/79617706
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞