平衡搜索树-BTree

B树是一种适合外查找的树,是一种平衡 的多叉树。
一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足一下性质:
1。根节点至少有两个孩子
2。每个非根节点有[M/2,M]个孩子
3。每个非根节点有[M/2-1,M-1]个关键字,并且以升序排列
4。每个节点孩子的数量比关键字的数量多一个。
5。 key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间
6。 所有的叶子节点都在同一层
平衡搜索树-BTree_第1张图片
平衡搜索树-BTree_第2张图片
下面用代码来实现一下B树

#pragma once

template<class K, class V, size_t M>
struct BTreeNode
{
    pair _kvs[M];   // 多开一个空间,方便分裂
    BTreeNode* _subs[M+1];
    BTreeNode* _parent;

    size_t _size; // 关键字的数量

    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>
class BTree
{
    typedef BTreeNode Node;
public:
    BTree()
        :_root(NULL)
    {}

    pairint> Find(const K& key)
    {
        Node* parent = NULL;
        Node* cur = _root;
        while (cur)
        {
            size_t i = 0;
            while(i < cur->_size)
            {
                if (cur->_kvs[i].first > key) // 在[i]的左树
                    break;
                else if (cur->_kvs[i].first < key) // 在后面
                    ++i;
                else
                    return make_pair(cur, i);
            }

            parent = cur;
            cur = cur->_subs[i];
        }

        return make_pair(parent, -1);
    }

    bool Insert(const pair& kv)
    {
        if (_root == NULL)
        {
            _root = new Node;
            _root->_kvs[0] = kv;
            _root->_size = 1;

            return true;
        }

        pairint> ret = Find(kv.first);
        if (ret.second >= 0)
        {
            return false;
        }

        Node* cur = ret.first;
        pair newKV = kv;
        Node* sub = NULL;

        // 往cur插入newKV, sub
        while (1)
        {
            InsertKV(cur, newKV, sub);

            if (cur->_size < M)
            {
                return true;
            }
            else 
            {
                // 分裂
                Node* newNode = DivideNode(cur);

                pair midKV = cur->_kvs[cur->_size/2];
                cur->_size -= (newNode->_size+1);

                // 1.根节点分裂
                if (cur == _root)
                {
                    _root = new Node;
                    _root->_kvs[0] = midKV;
                    _root->_size = 1;
                    _root->_subs[0] = cur;
                    _root->_subs[1] = newNode;
                    cur->_parent = _root;
                    newNode->_parent = _root;
                    return true;
                }
                else
                {
                    sub = newNode;
                    newKV = midKV;
                    cur = cur->_parent;
                }
            }
        }
    }

    Node* DivideNode(Node* cur)
    {
        Node* newNode = new Node;
        int mid = cur->_size/2;

        size_t j = 0;
        size_t i = mid+1;
        for (; i < cur->_size; ++i)
        {
            newNode->_kvs[j] = cur->_kvs[i];
            newNode->_subs[j] = cur->_subs[i];
            if(newNode->_subs[j])
                newNode->_subs[j]->_parent = newNode;
            newNode->_size++;
            j++;
        }

        newNode->_subs[j] = cur->_subs[i];
        if(newNode->_subs[j])
            newNode->_subs[j]->_parent = newNode;

        return newNode;
    }

    void InsertKV(Node* cur, const pair& kv, Node* sub)
    {
        int end = cur->_size-1;
        while (end >= 0)
        {
            if (cur->_kvs[end].first > kv.first)
            {
                cur->_kvs[end+1] = cur->_kvs[end];
                cur->_subs[end+2] = cur->_subs[end+1];
                --end;
            }
            else
            {
                break;
            }
        }

        cur->_kvs[end+1] = kv;
        cur->_subs[end+2] = sub;
        if(sub)
            sub->_parent = cur;

        cur->_size++;
    }

    void InOrder()
    {
        _InOrder(_root);
        cout<void _InOrder(Node* root)
    {
        if (root == NULL)
            return;

        Node* cur = root;
        size_t i = 0;
        for (; i < cur->_size; ++i)
        {
            _InOrder(cur->_subs[i]);
            cout<_kvs[i].first<<" ";
        }

        _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();
}

template<class K, size_t M>
struct BPTreeNonLeafNode
{
    K _keys[M];
    void* _subs[M];
    BPTreeNonLeafNode* _parent;
    size_t _size;
};

template<class K, class V, size_t M>
struct BPTreeLeafNode
{
    pair kvs[M];
    BPTreeLeafNode* _next;
    BPTreeNonLeafNode* _parent;
    size_t _size;
};

template<class K, class V, size_t M>
class BPTree
{
    typedef BPTreeLeafNode LeafNode;
    typedef BPTreeLeafNode NonLeafNode;
private:
    NonLeafNode* _root;
    LeafNode* _head;
};

B+树
B+树是对B树的一种变形树,它与B树的差异在于:
有k个子结点的结点必然有k个关键码;
非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。
树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。
平衡搜索树-BTree_第3张图片
B+树更适合文件索引系统
B*树
平衡搜索树-BTree_第4张图片
  B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3
(代替B+树的1/2);
       B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据
复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父
结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;
       B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分
数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字
(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之
间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;
       所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

你可能感兴趣的:(数据结构)