大师兄的数据结构学习笔记(十一): B树

大师兄的数据结构学习笔记(十): 伸展树
大师兄的数据结构学习笔记(十二): 红黑树

一、关于B树

1. 分级存储
  • 在计算机中,为了满足容量和访问速度的需求,通常用内存(高速度)和硬盘(大容量)进行分级存储。
  • 内存的访问速度大致为纳秒(ns)而硬盘位毫秒(ms),相差5-6个数量级, 应尽可能减少对外存(硬盘)的访问。
2. 多路搜索树
  • 外部存储器具备的另一特性:适合批量式访问,即读取物理地址连续的1000个字节和读取几个字节没区别。
  • 所以,应使用时间成本相对较低的多次内存操作,替代时间成本相对较高的单次外存(硬盘)操作。
  • 为此,我们需要改造二叉搜索树多路搜索树,让子树大于或等于两个,让树由"瘦高"变"矮胖"。
  • 因此,在多路搜索树中,各节点与其左、右孩子合并为大节点, 节点到左、右孩子的分支转化为大节点的内部搜索。
  • 多路搜索树在查找等搜索时,搜索每下降一层,都以大节点为单位从外存(硬盘)读取一组关键码。
3. B树
  • 所谓m阶的B树(B-tree),即m路的多路搜索树(m\geq2)
  • 每个节点最多有m个孩子。
  • 除了根节点和叶子节点外,其它每个节点至少有m/2个孩子。
  • 若根节点不是叶子节点,则至少有2个孩子。
  • 所有叶子节点都在同一层,且不包含其它关键字信息
  • 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)
  • 关键字的个数n满足: m/2-1 <= n <= m-1
  • ki(i=1,…n)为关键字,且关键字升序排序。
  • Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)
  • B树的最低搜索效率是O\log_2N

二、实现B树

1. 节点结构
template<class T>
struct Node
{
    int count;      //统计
    T* key;         //关键值列表
    Node** child;   //子节点的列表
    bool leaf;      //是否为叶结点
};
2. BTree类
template<class T>
class BTree
{
public:
    BTree();                //构造函数
    ~BTree();               //析构函数
    bool Insert(int key);   //插入节点
    bool Delete(int key);   //删除节点
    void Display();         //打印树
private:
    Node<T>* search(Node<T>* pNode, int key); //查找节点
    Node<T>* AllocateNode(); //重置节点
    void SplitChild(Node<T>* pParent, Node<T>* pChild, int index);
    Node<T>* unionChild(Node<T>* pParent, Node<T>* pCLeft, Node<T>* pCRright, int index); //合并大节点
    void InsertNonfull(Node<T>* pNode, int key);
    int Max(Node<T>* pNode); //最大结点
    int min(Node<T>* pNode); //最小结点
    bool DeleteNonHalf(Node<T>* pNode, int key);
    void DellocateNode(Node<T>* pNode);
    void DeleteTree(Node<T>* pNode);
    void print(Node<T>* pNode);
private:
    Node<T>* root; //根节点
    int M; // 度数 
};
3. 方法实现
template<class T>
//构造函数
BTree<T>::BTree() :root(nullptr), M(4)
{

};

template<class T>
//析构函数
BTree<T>::~BTree()
{
    DeleteTree(root);
    delete root;
};

template<class T>
//插入节点
bool BTree<T>::Insert(int key)
{
    Node<T>* r = root;
    if (!r)
    {
        r = AllocateNode();
        r->leaf = true;
        r->count = 0;

        root = r;
    }

    if (r && r->count == (2 * M - 1))
    {
        Node<T>* s = AllocateNode();
        root = s;
        s->leaf = false;
        s->count = 0;
        s->child[1] = r;
        SplitChild(s, r, 1);
        InsertNonfull(s, key);
    }
    else
    {
        InsertNonfull(r, key);
    }

    return true;
};

template<class T>
//删除节点
bool BTree<T>::Delete(int key)
{
    return DeleteNonHalf(root, key);
};

template<class T>
//删除节点
void BTree<T>::Display()
{
    print(root);
};

template<class T>
// 查找节点
Node<T>* BTree<T>::search(Node<T>* pNode, int key)
{
    int i = 1;
    while (i <= pNode->count && key > pNode->key[i])
    {
        i++;
    }

    if (i < pNode->count && key == pNode->key[i])
        return pNode->child[i];

    if (pNode->leaf)
        return nullptr;
    else
        return search(pNode->child[i], key);
}

template<class T>
// 重置节点
Node<T>* BTree<T>::AllocateNode()
{
    Node<T>* pTemp = new Node<T>;
    pTemp->key = new T[2 * M];
    pTemp->child = new Node<T>* [2 * M + 1];

    for (int i = 0; i < 2 * M; i++)
    {
        pTemp->key[i] = 0;
        pTemp->child[i] = nullptr;
    }
    pTemp->child[2 * M] = nullptr;

    return pTemp;
}

template<class T>
void BTree<T>::SplitChild(Node<T>* pParent, Node<T>* pChild, int index)
{
    Node<T>* pChildEx = AllocateNode();
    pChildEx->leaf = pChild->leaf;
    pChildEx->count = M - 1;

    for (int j = 1; j < M; j++)
    {
        pChildEx->key[j] = pChild->key[j + M];
    }

    if (!pChild->leaf)
    {
        for (int j = 1; j <= M; j++)
            pChildEx->child[j] = pChild->child[j + M];
    }

    pChild->count = M - 1;

    for (int j = pParent->count + 1; j > index; j--)
    {
        pParent->child[j + 1] = pParent->child[j];
    }
    pParent->child[index + 1] = pChildEx;

    for (int j = pParent->count + 1; j >= index; j--)
    {
        pParent->key[j + 1] = pParent->key[j];
    }
    pParent->key[index] = pChild->key[M];
    pParent->count++;
}

template<class T>
//合并大节点
Node<T>* BTree<T>::unionChild(Node<T>* pParent, Node<T>* pCLeft, Node<T>* pCRight, int index)
{
    for (int i = 1; i < M; i++)
    {
        pCLeft->key[M + i] = pCRight->key[i];
    }
    pCLeft->key[M] = pParent->key[index];

    for (int i = 1; i <= M; i++)
    {
        pCLeft->child[M + i] = pCRight->child[i];
    }
    pCLeft->count = 2 * M - 1;

    for (int i = index; i < pParent->count; i++)
    {
        pParent->key[i] = pParent->key[i + 1];
    }

    for (int i = index + 1; i <= pParent->count; i++)
    {
        pParent->child[i] = pParent->child(i + 1);
    }
    pParent->count--;

    DellocateNode(pCRight);

    if (pParent->count == 0)
    {
        DellocateNode(root);
        root = pCLeft;
    }
    return pCLeft;
}

template<class T>
void BTree<T>::InsertNonfull(Node<T>* pNode, int key)
{
    int i = pNode->count;

    if (pNode->leaf)
    {
        while (i >= 1 && key < pNode->key[i])
        {
            pNode->key[i + 1] = pNode->key[i];
            i--;
        }

        pNode->key[i + 1] = key;
        pNode->count++;
    }
    else
    {
        while (i >= 1 && key < pNode->key[i])
        {
            i--;
        }
        i++;

        if (pNode->child[i]->count == (2 * M - 1))
        {
            SplitChild(pNode, pNode->child[i], i);
            if (key > pNode->key[i])
                i++;
        }

        InsertNonfull(pNode->child[i], key);
    }
}

template<class T>
// 最大结点
int BTree<T>::Max(Node<T>* pNode)
{
    while (!pNode->leaf)
    {
        pNode = pNode->child[pNode->count + 1];
    }
    return pNode->key[pNode->count];
}

template<class T>
//最小结点
int BTree<T>::min(Node<T>* pNode)
{
    while (!pNode->leaf)
    {
        pNode = pNode->child[1];
    }
    return pNode->key[1];
}

template<class T>
bool BTree<T>::DeleteNonHalf(Node<T>* pNode, int key)
{
    int i = 1;

    while (i <= pNode->count && key > pNode->key[i])
        i++;

    if (pNode->leaf) //如果是叶结点
    {
        if (i <= pNode->count && key == pNode->key[i])
        {
            for (int j = i; j < pNode->count; j++)
            {
                pNode->key[j] = pNode->key[j + 1];
            }
            pNode->count--;
            return true;
        }
        else
        {
            return false;
        }
    }

    if (i <= pNode->count && key == pNode->key[i]) //如果是子结点
    {
        if (pNode->child[i]->count >= M)
        {
            key = Max(pNode->child[i]);
            pNode->key[i] = key;
            return DeleteNonHalf(pNode->child[i], key);
        }
        else if (pNode->child[i + 1]->count >= M)
        {
            key = Min(pNode->child[i + 1]);
            pNode->key[i] = key;
            return DeleteNonHalf(pNode->child[i + 1], key);
        }
        else
        {
            Node<T> pChild = unionChild(pNode, pNode->child[i], pNode->child[i + 1], i);
            return DeleteNonHalf(pChild, key);
        }
    }
    else if (pNode->child[i]->count == M - 1) //根节点
    {
        if (i > 1 && pNode->child[i - 1]->count >= M)
        {
            Node<T>* pMidNode = pNode->child[i];
            Node<T>* pPreNode = pNode->child[i - 1];

            int preNode_keyCount = pPreNode->count;

            for (int j = pMidNode->count + 1; j > 1; j--)
            {
                pMidNode->key[j] = pMidNode->key[j - 1];
            }
            pMidNode->key[1] = pNode->key[i - 1];

            for (int j = pMidNode->count + 2; j > 1; j--)
            {
                pMidNode->child[j] = pMidNode->child[j - 1];
            }
            pMidNode->child[1] = pPreNode->child[preNode_keyCount + 1];
            pMidNode->count++;

            pNode->key[i - 1] = pPreNode->key[preNode_keyCount];

            pPreNode->key[preNode_keyCount] = 0;
            pPreNode->key[preNode_keyCount + 1] = nullptr;
            pPreNode->count--;

            return DeleteNonHalf(pMidNode, key);
        }
        else if (i <= pNode->count && pNode->child[i + 1]->count >= M)
        {
            Node<T>* pMidNode = pNode->child[i];
            Node<T>* pNextNode = pNode->child[i + 1];

            int nextNode_keyCount = pNextNode->count;
            int midNode_keyCount = pMidNode->count;

            pMidNode->key[midNode_keyCount + 1] = pNode->key[i];
            pMidNode->child[midNode_keyCount + 2] = pNextNode->child[1];
            pMidNode->count++;

            pNode->key[i] = pNextNode->key[i];

            for (int j = 1; j < nextNode_keyCount; j++)
            {
                pNextNode->key[j] = pNextNode->key[j + 1];
            }
            for (int j = 1; j < nextNode_keyCount;j++)
            {
                pNextNode->child[j] = pNextNode->child[j + 1];
            }
            pNextNode->count--;

            return DeleteNonHalf(pMidNode, key);
        }
        else
        {
            if (i > pNode->count)
            {
                i--;
            }
            Node<T>* pChild = unionChild(pNode, pNode->child[i], pNode->child[i + 1], i);
            return DeleteNonHalf(pChild, key);
        }
    }
    return DeleteNonHalf(pNode->child[i], key);
}

template<class T>
void BTree<T>::DellocateNode(Node<T>* pNode)
{
    delete[] pNode->key;
    delete[] pNode->child;
    delete pNode;
}

template<class T>
void BTree<T>::DeleteTree(Node<T>* pNode)
{
    if (pNode->leaf)
    {
        delete[] pNode->key;
        delete[] pNode->child;
    }
    else
    {
        for (int i = 1; i <= pNode->count + 1; i++)
        {
            DeleteTree(pNode->child[i]);
            delete pNode->child[i];
        }

        delete[] pNode->key;
        delete[] pNode->child;
    }
}

template<class T>
void BTree<T>::print(Node<T>* pNode)
{
    if (pNode->leaf)
    {
        cout << "leaf count = "<< pNode->count << ":";
        for (int i = 1; i <= pNode->count; i++)
        {
            cout << pNode->key[i] << ",";
        }
        cout << endl;
    }
    else
    {
        for (int i = 1; i <= pNode->count + 1; i++)
        {
            //cout << pNode->child[i] << endl;
            print(pNode->child[i]);
        }
        cout << "inner node count:" << pNode->count << ":";

        for (int i = 1; i < pNode->count; i++)
        {
            cout << pNode->key[i] << ",";
        }
        cout << endl;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容