美文网首页
多路查找树(B树)

多路查找树(B树)

作者: lkmc2 | 来源:发表于2018-08-27 23:27 被阅读97次

多路查找树:树的每一个节点的孩子数可以多于两个,而且每一个节点处可以存储多个元素。

概念介绍
2-3树:是多路查找树的一种,其中每一个节点都具有两个孩子(2节点)或三个孩子(3节点)。
2节点:一个2节点包含一个元素和两个孩子(或没有孩子)。与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素。和二叉排序树的区别在于,要么2节点的两个孩子都存在,要么这两个孩子都不存在,不会出现只有一个孩子的情况。
3节点:一个3节点包含一个元素和三个孩子(或没有孩子)。与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素,中间子树包含介于左右子树之间的元素。和二叉排序树的区别在于,要么3节点的三个孩子都存在,要么这三个孩子都不存在,不会出现只有一、两个孩子的情况。

// 多路查找树(B树)
#include <stdio.h>
#include <malloc.h>

#define TRUE 1    // 返回值为真
#define FALSE 0   // 返回值为假

#define m 3 // B树的阶,暂时设置为3
#define N 17 // 数据元素个数

typedef int Status; // 函数返回结果类型

// B树结构
typedef struct BTNode {
    int keynum; // 节点中关键字个数(节点可存储多个元素的值)
    struct BTNode *parent; // 指向双亲节点的指针
    struct Node { // 节点结构
        int key; // 关键字向量
        struct BTNode *ptr; // 子树指针向量
        int recptr; // 记录指针向量
    } node[m + 1];
} BTNode, *BTree;

// B树查找结果结构
typedef struct {
    BTNode *pt; // 指向找到的节点
    int i; // 1..m,在节点中的关键字序号
    int tag; // 是否找到标记,1表示查找成功,2表示查找失败
} Result;

/**
 * 在树中节点查找最接近但小于关键字K的元素的下标
 * 在p->node[1..keynum].key中查找i,使得p->node[i].key < K < p->node[i+1].key
 * @param p B树
 * @param K 关键字
 * @return 最接近但小于关键字K的元素的下标
 */
int Search(BTree p, int K) {
    int i = 0; // 用来记录最接近但小于关键字K的元素的下标
    int j; // 用来遍历元素

    // 遍历树中的所有元素,找到最接近但小于关键字K的元素的下标
    for (j = 1; j <= p->keynum; j++) {
        // 如果该节点的值小于关键字K
        if (p->node[j].key <= K) {
            i = j; // 记录该元素的下标
        }
    }
    return i; // 返回最接近但小于关键字K的元素的下标
}

/**
 * 在m阶B树T上查找关键字K,返回Result结果(pt, i, tag)
 * 若查找成功,则Result中tag=1,指针pt所指节点中第i个关键字等于K,
 * 查找不成功,Result中tag=0,等于K的关键字应插入在指针pt所值节点中第i和第i+1个关键字之间
 * @param T B树
 * @param K 关键字
 * @return B树查找结果
 */
Result SearchBTree(BTree T, int K) {
    BTree p = T; // B树p指向T
    BTree q = NULL; // B树q
    Status found = FALSE; // 是否找到元素
    int i = 0; // 用于记录找到元素的下标
    Result r; // B树查找结果

    // 未遍历完树T,且未找到值等于关键字K的元素
    while (p && !found) {
        // 在树中节点查找最接近但小于关键字K的元素的下标
        i = Search(p, K);

        // 查找到的元素的下标大于0,并且元素的值等于关键字K
        if (i > 0 && p->node[i].key == K) {
            found = TRUE; // 标记找到元素
        } else { // 未找到值等于K的元素
            q = p; // q指向当前树节点p
            p = p->node[i].ptr; // p指向下一个节点
        }
    }
    r.i = i; // 返回结果的在节点中的关键字序号设置为i

    if (found) { // 查找成功
        r.pt = p; // pt指向节点中第i个关键字等于K元素
        r.tag = 1; // tag=1表示查找成功
    } else { // 查找失败
        r.pt = q; // pt指向q表示指向将进行插入的位置
        r.tag = 0;  // tag=0表示查找失败
    }
    return r;
}

/**
 * 将r->key、r和ap分别插入到q->key[i+1]、q->recptr[i+1]和q->ptr[i+1]中
 * @param q B树
 * @param i 插入位置下标
 * @param key 插入的值
 * @param ap 新插入节点指向的下一个节点
 */
void Insert(BTree *q, int i, int key, BTree ap) {
    int j; // 用来遍历元素

    // 将插入位置的元素向后移动一位,空出(*q)->node[i+1]的位置
    for (j = (*q)->keynum; j > i; j--) {
        (*q)->node[j + 1] = (*q)->node[j];
    }

    (*q)->node[i + 1].key = key; // 设置新插入节点的值
    (*q)->node[i + 1].ptr = ap; // 设置新插入节点指向的的子树节点
    (*q)->node[i + 1].recptr = key; // 设置新插入节点的记录的指针向量
    (*q)->keynum++; // 树中节点个数增加
}

/**
 * 将节点q分拆成两个节点,前一半保留,后一遍移入新生节点ap
 * @param q 原节点,前一半的值保留在此节点
 * @param aq 新节点,后一半的值移动到此节点
 */
void split(BTree *q, BTree *ap) {
    int i, s = (m + 1) / 2; // 用来遍历元素,s是节点个数的一半
    *ap = (BTree) malloc(sizeof(BTNode)); // 为新节点分配存储空间
    (*ap)->node[0].ptr = (*q)->node[s].ptr; // 新节点ap指向原节点的下一个元素

    // 将后一般的元素移动到新节点
    for (i = s + 1; i <= m; i++) {
        (*ap)->node[i - s] = (*q)->node[i]; // 将后一半的元素值设置到新节点

        if ((*ap)->node[i - s].ptr) { // 若新节点的子树指针有值
            (*ap)->node[i - s].ptr->parent = *ap; // 将元素的父节点指针指向ap
        }
    }

    (*ap)->keynum = m - s; // qp树节点个数等于
    (*ap)->parent = (*q)->parent;
    (*q)->keynum = s - 1; // q树节点个数减1
}

/**
 * 生成新的根节点
 * @param T
 * @param key 节点值
 * @param ap
 */
void NewRoot(BTree *T, int key, BTree ap) {
    BTree p; // 新的根节点
    p = (BTree) malloc(sizeof(BTNode)); // 为新的根节点分配存储空间
    p->node[0].ptr = *T; // 根节点的第0个元素指向原来的T节点(成为新的根节点的子元素)
    *T = p; // T节点指向新的根节点

    // 如果新节点第0个元素的指向的值非空
    if ((*T)->node[0].ptr) {
        // 让新节点第0个元素的指向的值的父节点为T
        (*T)->node[0].ptr->parent = *T;
    }

    (*T)->parent = NULL; // 设置新的根节点无双亲
    (*T)->keynum = 1; // 设置根节点的元素数量为1
    (*T)->node[1].key = key; // 设置根节点元素的值
    (*T)->node[1].recptr = key; // 设置记录指针向量为key
    (*T)->node[1].ptr = ap; // 设置根节点第1个元素指向ap节点

    // 如果新节点第1个元素的指向的值非空
    if ((*T)->node[1].ptr) {
        // 让新节点第1个元素的指向的值的父节点为T
        (*T)->node[1].ptr->parent = *T;
    }
}

/**
 * 在m阶B上T上节点*q的key[i]与key[i+1]之间插入关键字K的指针r
 * 若引起节点过大,则沿双亲链进行必要的节点分拆调整,使T仍是m阶B树
 * @param T B树
 * @param key 关键字
 * @param q
 * @param i
 */
void InsertBTree(BTree *T, int key, BTree q, int i) {
    BTree ap = NULL; // 
    Status finished = FALSE; // 是否插入完成的标记
    int s; // 用来获取节点中间位置的下标
    int rx; // 用于 获取中间位置的记录数

    rx = key; // 设置记录数等于关键字

    // 当q节点非空,插入未完成
    while (q && !finished) {
        // 将值为rx的元素插入到树qe的第i个位置
        Insert(&q, i, rx, ap);

        // 若插入后B树q的节点数小于阶数
        if (q->keynum < m) {
            finished = TRUE; // 插入完成
        } else { // B树q的节点数大于等于m,将分拆该节点
            s = (m + 1) / 2; // 获取中间节点下标
            rx = q->node[s].recptr; // 获取中间位置的记录数
            split(&q, &ap); // 对节点进行分拆

            q = q->parent; // q指向自己的双亲节点
            if (q) {
                i = Search(q, key); // 在双亲节点*q中查找rx->key的插入位置
            }
        }
    }

    // 当插入未完成
    if (!finished) {
        NewRoot(T, rx, ap); // 创建新的根节点
    }
}

/**
 * 打印B树中第i个节点的值
 * @param p B树
 * @param i 节点下标
 */
void print(BTNode p, int i) {
    printf("(%d)", p.node[i].key);
}

int main() {
    int r[N] = {22, 16, 41, 58, 8, 11, 12, 16, 17, 22, 23, 31, 41, 52, 58, 59, 61};
    BTree T = NULL; // B树
    Result result; // B树查找结果结构
    int i; // 用来遍历元素
    
    for (i = 0; i < N; i++) {
        // 在B树T中搜索数组r中下标为i的元素,并返回搜索结果
        result = SearchBTree(T, r[i]);
        // 如果r中下标为i的元素在B中T中不存在,将该元素插入B中T中
        if (!result.tag) {
            InsertBTree(&T, r[i], result.pt, result.i);
        }
    }

    printf("\n请输入待查找记录的关键字:");
    scanf("%d", &i);
    result = SearchBTree(T, i); // 在B树中搜索该关键字,并获取结果

    // 如果该结果标记为已找到
    if (result.tag) {
        print(*(result.pt), result.i); // 打印B树中第i个节点的值
    } else {
        printf("未找到该关键字");
    }

    return 0;
}
运行结果1 运行结果2

相关文章

  • 多路查找树(B树)

    多路查找树:树的每一个节点的孩子数可以多于两个,而且每一个节点处可以存储多个元素。 概念介绍2-3树:是多路查找树...

  • 多路查找树(B树)

    其每一个节点的孩子数,可以多于两个,且每一个节点可以存储多个元素 2-3树 其中的每一节点都具有两个孩子(称为2结...

  • 多路查找树(B树)

    多路查找树(multi-way search tree):其中每一个结点的孩子数可以多于两个,且每一个结点处可以存...

  • 数据结构与算法系列(B树)

    B树 B树即平衡查找树,一般理解为平衡多路查找树,也称为B-树、B_树。是一种自平衡树状数据结构,能对存储的数据进...

  • B/B+ 树和MySQL

    B 树 B树是一种 多路平衡查找树, B是平衡(Balance)的意思,不是 Binary,也被翻译成 B-树(B...

  • B树和B+树

    B树的定义: B树是 多路 平衡 查找树. B树必须满足的特点: 先上一张图: 下图是一个3阶B树. m阶B树必须...

  • B+Tree 介绍与在 Mysql 中的应用

    1、B树简绍 B 树又称平衡多路查找树。 1.1、用阶来描述 B 树 一个 m 阶的 B 树具有一下特点: 1 每...

  • B-树/B+树

    B-树(Balance树)和B+树应用与数据库索引,是m叉的多路平衡查找树。 1. 性质分析 1.1 M阶B-树 ...

  • B树和B+树的插入和删除

    一. B树 1.1 B树的定义 B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表...

  • B树结构参考

    B树的节点包括:Key键值,Index索引值,Data数据 B树维基百科参考 B树别称:多路查找平衡树,多分支的平...

网友评论

      本文标题:多路查找树(B树)

      本文链接:https://www.haomeiwen.com/subject/aqexwftx.html