【數據結構】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樹的插入過程:
這裏寫圖片描述
這裏寫圖片描述這裏寫圖片描述這裏寫圖片描述
代碼實現過程:
1.首先設定結構體,實際只用到兩個關鍵碼,但是爲了交換簡單,我們設置三個關鍵碼,孩子指針域始終比關鍵碼多一個。
這裏寫圖片描述
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;
}
發佈了70 篇原創文章 · 獲贊 92 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章