1. 程式人生 > >B樹的基本定義與B樹的插入

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;
}

這裡寫圖片描述
圖可能有點醜,多多包涵。