找回密码
 注册

QQ登录

只需一步,快速开始

查看: 4973|回复: 12
收起左侧

[原创] 无聊ing。发个技术灌水贴子。。——B树的实现

[复制链接]

该用户从未签到

发表于 2010-9-22 00:35 | 显示全部楼层 |阅读模式
B树
平衡的m阶查找树,其中:
1. 树中的每个节点至多含有m棵子树
2. 根节点若不是叶子节点,则其含有至少两棵子树
3. 除根节点以外的非叶子节点,其至少含有[m/2]棵子树
4. 所有的非叶子节点,含有以下的数据域:
   (n,A1,K1,R1,...,An,Kn,Rn,A[n+1])
    n是节点里含有的关键字个数,n+1为子树个数
    An是指向子树根节点的指针,其中A[n]所指向子树的所有关键字都小于Kn,
    A[n+1]所指向子树的所有关键字都大于Kn
    Kn是关键字
    Rn是指向Kn关键字对应的记录
其中关键字是有序的。

B树的一种变体B+树是流行的数据库文件结构。

B树的查找和插入删除算法,实现如下,其中查找和插入算法在严蔚敏的C语言数据结构书中有介绍,而删除算法不论在严蔚敏的书中,还是在MIT的算法导论中,均只有概念性的描述,而没有具体的实现参考。

以下程序随机产生100个0-100之间的整数,然后用这些数构造一棵5阶的B树,最后依次从树中删除各个数,直至树为空。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define m 5 // 5阶B树
typedef unsigned int key_t;
typedef unsigned long val_t; // B树作为索引,节点里的记录作为一个数据文件的偏移

struct __btree_node
{
    int n;
    struct __btree_node *parent;
    key_t key
&#173; // 实际包含n-1个key,最多m-1个key
    struct __btree_node *A[m + 1]; // 实际包含n个A*,最多m个A*
    val_t R
&#173;
};
typedef struct __btree_node bt_node, *bt_node_ptr, *bt_ptr;

bt_ptr bt_root;

bt_node_ptr bt_search(bt_node_ptr p, key_t k, int deep, int *which, int *got)
{
    bt_node_ptr cur = p, ancestor = p;
    int n, i;

    if (got)
        *got = 0;
    while (cur)
    {
        n = cur->n;

        for (i = 0; i < n; i++)
        {
            *which = i;
            if (k < cur->key)
            
{
                ancestor = cur;
                if (deep)
                    cur = cur->A;
                else
                    cur = NULL;
                break;
            }
            else if (k == cur->key)
            {
                if (got)
                    *got = 1;
                else
                    (*which)++;
                return cur;
            }
        }
        if (i >= n)
        {
            *which = n;
            ancestor = cur;
            if (deep)
                cur = cur->A[n];
            else
                cur = NULL;
        }
    }
    // 当查找失败,返回父节点,为了插入方便
    return ancestor;
}

int bt_insert(bt_node_ptr *p, key_t k)
{
    int which, got = 1, over = 0;
    int n, i;
    key_t k0 = k;
    bt_node_ptr a = *p, t, a0 = NULL;

    while (!over)
    {
        t = bt_search(a, k0, got, &which, &got);
        if (got)
            return !got; // 已有相同节点,不必插入

        if (t == NULL) // 树为空
        {
            bt_node_ptr parent = (bt_node_ptr) malloc(sizeof(bt_node));

            // 新建一个树根节点
            parent->n = 1;
            parent->parent = NULL;
            parent->key[0] = k0;
            parent->A[0] = *p;
            parent->A[1] = a0;

            if (a0)
                a0->parent = parent;
            if (*p)
                (*p)->parent = parent;
            *p = parent;

            over = 1;
        }
        else // 查找失败,返回应插入位置的父节点
        {
            n = t->n;

            // 直接插入
            for (i = n; i > which; i--)
            {
                t->key = t->key[i - 1];
                t->A[i + 1] = t->A;
            }
            t->key[which] = k0;
            t->A[which + 1] = a0;

            if (a0)
                a0->parent = t;
            ++(t->n);

            if (n + 1 > m - 1) // 需要分裂节点
            {
                // a指向的节点已经含有了m-1个key
                // 如果继续插入key,则节点将变为:
                // m,A0,K0,A1,K1,...,K[m-1],A
&#173;
                //
                // 为此,分裂a指向的节点为a和a':
                // a:  [m/2]-1,A0,K0,...,K[m/2-2],A[m/2-1]
                // a': m-[m/2],A[m/2],K[m/2],...,K[m-1],A
&#173;
                //
                // 当m=4时,如下:
                // a:  1,A0,K0,A1
                // a': 2,A2,K2,A3,K3,A4
                //
                // 而K[m/2-1]和指向a'的指针插入到父节点中
                // 另外,这个分裂节点的过程显然也适合父节点
                int d = (m + 1) / 2, c = d - 1;

                // 当m=4时,d=2,c=1

                a0 = (bt_node_ptr) malloc(sizeof(bt_node));
                k0 = t->key[c];

                // a节点
                t->n = c;

                // a'节点
                a0->n = m - d;
                for (i = d; i < m; i++)
                {
                    a0->A[i - d] = t->A;
                    if (a0->A[i - d])
                        a0->A[i - d]->parent = a0;
                    a0->key[i - d] = t->key;
                }
                a0->A[m - d] = t->A
&#173;
                if (a0->A[m - d])
                    a0->A[m - d]->parent = a0;

                // 插入父节点
                // 需要判断父节点是否为空
                // 即,当前节点是否为根节点

                // 当根节点需要分裂时,查找根节点的父节点的结果也是返回空
                a = t->parent;
            }
            else
                over = 1;
        }
    }

    return !got;
}

int bt_delete(bt_node_ptr *p, key_t k)
{
    int w, q, over = 0;
    int n, d, c, i;
    key_t k0 = k;
    bt_node_ptr a = *p, t;

    // 先搜索这个给定的key
    t = bt_search(a, k0, 1, &w, &q);

    if (!q)
        return q;

    // 如果找到,则将这个key对应的节点删除
    // 1.非叶子节点
    if (t->A[w] || t->A[w + 1])
    {
        // 找出待删节点右子树的最小节点
        // (因为B树是平衡树,假设不会出现有左子树而无右子树的情况)
        bt_node_ptr child = t->A[w + 1];
        while (child->A[0])
            child = child->A[0];

        t->key[w] = child->key[0];
        // 接下来要删去右子树的最小节点
        t = child;
        w = 0;
    }

    d = (m + 1) / 2; // d=[m/2],当m=5时,d=3
    c = d - 1;

    if (w)
        w++;
    // 2.叶子节点,分为3种情况
    do
    {
        // 先删去待删节点
        n = t->n;
        q = n - 1;

        if (w == 0)
        {
            for (i = 0; i < q; i++)
            {
                t->key = t->key[i + 1];
                t->A = t->A[i + 1];
            }
            t->A = t->A[i + 1];
        }
        else
        {
            for (i = w - 1; i < q; i++)
            {
                t->key = t->key[i + 1];
                t->A[i + 1] = t->A[i + 2];
            }
        }

        // 如果回溯到根节点
        if (!(t->parent))
        {
            if (n < d)
            {
                *p = t->A[0];
                free(t);
                if (*p)
                    (*p)->parent = NULL;
            }
            else
                --(t->n);
            break;
        }

        if (n < d) // 待删节点的关键字个数小于d,需要合并节点
        {
            bt_node_ptr bro;

            bt_search(t->parent, t->key[0], 0, &w, NULL);
            // b).待删节点的关键字个数小于d,而相邻节点不小于d
            if (w == 0)
            {
                bro = t->parent->A[w + 1];
                if (bro->n >= d)
                {
                    k0 = t->parent->key[0];
                    t->parent->key[0] = bro->key[0];
                    t->key[t->n - 1] = k0;
                    t->A[t->n] = bro->A[0];
                    if (t->A[t->n])
                        t->A[t->n]->parent = t;

                    n = --(bro->n);
                    for (i = 0; i < n; i++)
                    {
                        bro->key = bro->key[i + 1];
                        bro->A = bro->A[i + 1];
                    }
                    bro->A = bro->A[i + 1];
                    break;
                }
            }
            else
            {
                bro = t->parent->A[w - 1];
                if (bro->n >= d)
                {
                    k0 = t->parent->key[w - 1];
                    t->parent->key[w - 1] = bro->key[bro->n - 1];

                    for (i = q; i > 0; i--)
                    {
                        t->key = t->key[i - 1];
                        t->A[i + 1] = t->A;
                    }
                    t->A[1] = t->A[0];
                    t->key[0] = k0;
                    t->A[0] = bro->A[bro->n];
                    if (t->A[0])
                        t->A[0]->parent = t;

                    --(bro->n);
                    break;
                }
            }

            // c).待删节点里的关键字个数小于d,并且相邻节点也小于d
            if (w == 0)
            {
                for (i = 0; i < c; i++)
                {
                    bro->key[i + c] = bro->key;
                    bro->A[i + c + 1] = bro->A[i + 1];
                }
                bro->A[c] = bro->A[0];
                bro->key[c - 1] = t->parent->key[0];

                for (i = 0; i < c - 1; i++)
                {
                    bro->key = t->key;
                    bro->A = t->A;
                    if (bro->A)
                        bro->A->parent = bro;
                }
                bro->A = t->A;
                if (bro->A)
                    bro->A->parent = bro;
                free(t);

                bro->n <<= 1;
            }
            else
            {
                bro->key[c] = t->parent->key[w - 1];

                for (i = 0; i < c - 1; i++)
                {
                    bro->A[i + c + 1] = t->A;
                    bro->key[i + c + 1] = t->key;
                    if (bro->A[i + c + 1])
                        bro->A[i + c + 1]->parent = bro;
                }
                bro->A[i + c + 1] = t->A;
                if (bro->A[i + c + 1])
                    bro->A[i + c + 1]->parent = bro;
                free(t);

                bro->n <<= 1;
            }
            t = bro->parent;
        }
        else
        {
            --(t->n);
            over = 1;
        }
    } while (!over);

    return q;
}

void bt_destroy()
{
    if (bt_root)
    {
        bt_node_ptr cur = bt_root, sav_cur;
        int n, i;

        while (cur)
        {
            n = cur->n;
            for (i = 0; i <= n; i++)
            {
                if (cur->A)
                {
                    cur = cur->A;
                    cur->parent->A = NULL;
                    break;
                }
            }
            if (i > n)
            {
                sav_cur = cur->parent;
                free(cur);
                cur = sav_cur;
            }
        }
    }
}

//===========================================================================================
#define RECORD_CNT 100
int main()
{
    int i;
    int seq[RECORD_CNT];

    // B树
    bt_root = NULL;

    srand(time(NULL));
    for (i = 0; i < RECORD_CNT; i++)
        seq = rand() % RECORD_CNT;

    for (i = 0; i < RECORD_CNT; i++)
        bt_insert(&bt_root, seq);

    for (i = 0; i < RECORD_CNT; i++)
        bt_delete(&bt_root, seq);

    bt_destroy();

    return 0;
}
  • TA的每日心情
    开心
    2023-4-15 08:35
  • 签到天数: 462 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2010-9-22 01:03 | 显示全部楼层
    唉,你实在是太无聊了。。。。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2010-9-22 01:08 | 显示全部楼层
    B树是什么鸟树?看不懂
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2023-4-15 08:35
  • 签到天数: 462 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2010-9-22 01:11 | 显示全部楼层
    大概就是像你这种树吧。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2010-9-22 01:15 | 显示全部楼层
    哦,原来是玉树,好,够挺拔够临风,我喜欢
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2014-10-26 11:04
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2010-9-22 09:21 | 显示全部楼层
    哦,原来是玉树,好,够挺拔够临风,我喜欢
    荒原 发表于 2010-9-22 01:15
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2023-4-15 08:35
  • 签到天数: 462 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2010-9-22 22:06 | 显示全部楼层
    你别笑,你一笑,他就知道我说什么了
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2010-9-22 22:50 | 显示全部楼层
    人家妹妹笑笑咋了,你也忒霸道了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2023-4-15 08:35
  • 签到天数: 462 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2010-9-22 22:55 | 显示全部楼层
    怕伤了你的自尊心嘛
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2010-9-22 23:10 | 显示全部楼层
    怕伤了你的自尊心嘛
    吟风听月 发表于 2010-9-22 22:55

    你丫的已经伤了我的自尊心了,我靠
    回复 支持 反对

    使用道具 举报

    该用户从未签到

     楼主| 发表于 2010-9-22 23:32 | 显示全部楼层
    靠吧。。哥哥的肩膀借给你靠。。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2010-9-23 15:43 | 显示全部楼层
    光光,给我看看你的胸大肌
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2014-10-26 11:04
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2010-9-23 16:05 | 显示全部楼层
    俺再路过一下。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    QQ|小黑屋|《唐诗宋词》网站 ( 苏ICP备2021032776号 )

    GMT+8, 2024-11-25 11:27 , Processed in 0.082554 second(s), 18 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表