动态查找树 || B-树的c++实现

29 篇文章 5 订阅
27 篇文章 1 订阅

B-树是一棵平衡的多路查找树,它在文件系统中很有用。

一棵m阶的B-树或为空树或为满足以下特性的m叉树:

  • 树中的每个结点至多有m棵子树。

  • 若根结点不是叶子结点,则至少有两颗子树。

  • 除根结点外的所有非终端结点至少有Ceiling(m/2)(向上取整)棵子树

  • 所有的非终端结点中包含下列信息数据:
    (n, A0, K1, A1, K2, A2, … , Kn, An)

    • n表示有n个结点,Ki为关键字,Ai为指针。K1<…<Kn;指针Ai-1指向的结点的关键字均小于Ki,An所指向的结点的关键字均大于Kn。
  • 所有的叶子结点均出现在同一层次上,且不带信息(可以看作外部结点或者查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。

  • hpp

//
//  BTree.hpp
//  BTree
//
//  Created by peiyu wang on 2019/3/25.
//  Copyright © 2019 peiyu wang. All rights reserved.
//

#ifndef BTree_hpp
#define BTree_hpp

#include <stdio.h>
#include <cmath>
#include <iostream>
using namespace std;
/* 宏定义 */
#define M 3

/* 类型定义 */
typedef int BTElemType; //B树元素类型
typedef int KeyType;
struct BTNode
{
    int keynum;
    BTNode *parent; // 指向双亲结点
    KeyType key[M+1]; // 关键字向量,0号单元未用
    BTNode *ptr[M+1]; // 指向B树结点的指针
};

typedef BTNode* BTree;

/* B树查找结果类型 */
struct Result
{
    BTree pt; // 指向找到的结点
    int i; //1...n,关键字在结点中的序号(插入位置)
    int tag; // 1:查找成功,0:查找失败
};

/* 函数列表 */
// 创建B树
int CreateBTree(BTree &BT);
// 查找并返回查找信息
Result SearchBTree(BTree BT, KeyType key);
// 返回key在结点p中的次序,若不存在则返回0
int Search(BTree p, KeyType key);
// 将关键字key插入到B树中
int InsertKey(BTree &BT, KeyType key);
// 插入,在结点q的key[i]和key[i+1]之间插入关键字key
int InsertBTree(BTree &BT, KeyType key, BTree q, int i);
// 将x和ap分别插入到q->key[i+1]和q->ptr[i+1]
void Insert(BTree q, KeyType i, KeyType x, BTree ap);
// 以s为界,将q指向的结点分裂成q和ap所指向的两部分
void split(BTree q, int s, BTree &ap);
// 生成含信息(BT,x,ap)的新的根结点BT, 原BT和ap为子树指针,q初值为NULL。
void NewRoot(BTree &BT, int x, BTree ap);
// 从B树中删除关键字key
int DeleteKey(BTree &BT, KeyType K);
// 从B树中删除结点q中第i个关键字
int DeleteBTree(BTree &BT, BTree q, int i);
// 从B树中删除结点q中第i个关键字(特殊)
void Delete(BTree &BT, BTree q, int i);
// 找出BT中最小的关键字
int SearchMinKey(BTree BT, Result &R);
// 找出双亲结点: q为p的第j个孩子
int FoundParent(BTree q, BTree &p, int &i);
// 向左移动关键字和指针
int LeftMove(BTree old_ptr, int m, BTree new_ptr, int n, int len);
// 向右移动关键字和指针
int RightMove(BTree old_ptr, int m, BTree new_ptr, int n, int len);
// 层序输出B树
void PrintBT_Level(BTree BT);
// 中序输出B树
void PrintBT_InOrder(BTree BT);

#endif /* BTree_hpp */

  • cpp
//
//  BTree.cpp
//  BTree
//
//  Created by peiyu wang on 2019/3/25.
//  Copyright © 2019 peiyu wang. All rights reserved.
//

#include "BTree.hpp"
#include <queue>
// 创建B树
int CreateBTree(BTree &BT)
{
    int n;
    BT = NULL;
    cout << "请输入结点数目:" << endl;
    cin >> n;
    cout << "依次输入每个结点的关键值:" << endl;
    for (int i = 1; i <= n; i++)
    {
        KeyType key;
        cin >> key;
        InsertKey(BT, key);
    }
    return 1;
}

// 查找并返回查找信息
Result SearchBTree(BTree BT, KeyType key)
{
    Result R = {NULL, 0, 0};
    BTree p, q;
    int found, i;
    p = BT;
    q = NULL;
    found = false;
    i = 0;
    while (p && !found)
    {
        i = Search(p, key);
        if (i > 0 && p->key[i] == key)
        {
            found = true;
        }
        else
        {
            q = p;
            p = p->ptr[i];
        }
    }
    R.i = i;
    if (found)
    {
        R.pt = p;
        R.tag = 1;
    }
    else
    {
        R.pt = q;
        R.tag = 0;
    }
    return R;
}

// 返回key在结点p中的次序,若不存在则返回0
int Search(BTree p, KeyType key)
{
    int i, j;
    for (i = 0, j = 1; j <= p->keynum; j++)
    {
        if (p->key[j] <= key)
            i = j;
        else
            break;
    }
    return i;
}

// 将关键字key插入到B树中
int InsertKey(BTree &BT, KeyType key)
{
    Result R;
    R = SearchBTree(BT, key);
    if (R.tag == 0)
    {
        InsertBTree(BT, key, R.pt, R.i);
        return 1;
    }
    else
        return 0;
}

// 插入,在结点q的key[i]和key[i+1]之间插入关键字key
int InsertBTree(BTree &BT, KeyType key, BTree q, int i)
{
    KeyType x;
    BTree ap;
    int finished;
    int s;
    x = key;
    ap = NULL;
    finished = false;
    while (q && !finished)
    {
        Insert(q, i, x, ap);
        if (q->keynum < M)
            finished = true;
        else
        {
            s = ceil((double)M / 2);
            split(q, s, ap);
            x = q->key[s];
            q = q->parent;
            if (q)
                i = Search(q, x);
        }
    }
    if (!finished)
        NewRoot(BT, x, ap); //生成含信息(BT,x,ap)的新结点BT
    return 1;
}


// 将x和ap分别插入到q->key[i+1]和q->ptr[i+1]
void Insert(BTree q, KeyType i, KeyType x, BTree ap)
{
    int j;
    for (j = q->keynum; j > i; j--)
    {
        q->key[j+1] = q->key[j];
        q->ptr[j+1] = q->ptr[j];
    }
    q->key[i+1] = x;
    q->ptr[i+1] = ap;
    q->keynum++;
}

// 以s为界,将q指向的结点分裂成q和ap所指向的两部分
void split(BTree q, int s, BTree &ap)
{
    ap = new BTNode;
    ap->ptr[0] = q->ptr[s];
    for (int i = s+1; i <= M; i++)
    {
        ap->key[i-s] = q->key[i];
        ap->ptr[i-s] = q->ptr[i];
    }
    ap->keynum = M-s;
    q->keynum = s-1;
    ap->parent = q->parent;
    for (int i = 0; i <= ap->keynum; i++)
    {
        if (ap->ptr[i])
            ap->ptr[i]->parent = ap;
    }
    
}

// 生成含信息(BT,x,ap)的新的根结点BT, 原BT和ap为子树指针。
void NewRoot(BTree &BT, int x, BTree ap)
{
    BTree p = NULL;
    p = new BTNode;
    p->keynum = 1;
    p->parent = NULL;
    p->ptr[0] = BT;
    p->ptr[1] = ap;
    p->key[1] = x;
    if (p->ptr[0])
        p->ptr[0]->parent = p;
    if (p->ptr[1])
        p->ptr[1]->parent = p;
    BT = p;
}

// 从B树中删除关键字key
int DeleteKey(BTree &BT, KeyType key)
{
    Result R;
    R = SearchBTree(BT, key);
    if (R.tag == 1)
    {
        DeleteBTree(BT, R.pt, R.i);
        return 1;
    }
    else
        return 0;
    
}

// 从B树中删除结点q中第i个关键字
int DeleteBTree(BTree &BT, BTree q, int i)
{
    Result R = {q, i, 1};
    if (i < 1 || i > q->keynum)
        return 0;
    if (q->ptr[i])
        SearchMinKey(q->ptr[i], R);
    q->key[i] = R.pt->key[R.i];
    Delete(BT, R.pt, R.i);
    return 1;
}

// 从B树中删除结点q中第i个关键字(特殊)
void Delete(BTree &BT, BTree q, int i)
{
    int s, tag, order;
    BTree p, lc, rc;
    s = ceil((double)M/2);
    p = NULL; //q的父结点
    order = -1; //q为p的第order个关键字
    tag = 0;
    if (!FoundParent(q, p, order)) // q是p的第order个孩子
        tag = 1;
    else
    {
        if (q->keynum >= s)
            tag = 2;
        else
        {
            if (tag == 0 && order < p->keynum && p->ptr[order+1]->keynum >= s)
                tag = 3; // 右兄弟关键字个数 >= ceil(M/2)
            if (tag == 0 && order > 0 && p->ptr[order-1]->keynum >= s)
                tag = 4;
            if (tag == 0 && order < p->keynum && p->ptr[order+1]->keynum == s-1)
                tag = 5; // 右兄弟关键字个数 = ceil(M/2) - 1
            if (tag == 0 && order > 0 && p->ptr[order-1]->keynum == s-1)
                tag = 6;
        }
    }
    switch(tag)
    {
        case 1:
            if (q->keynum == 1 && i == 1)
            {
                BT = q->ptr[0];
                BT->parent = NULL;
                delete q;
                q = NULL;
                
            }
            else
            {
                LeftMove(q, i+1, q, i, q->keynum - i);
                q->keynum--;
            }
            break;
        case 2:
            LeftMove(q, i+1, q, i, q->keynum - i);
            q->keynum--;
            break;
        case 3:
            rc = p->ptr[order + 1];
            LeftMove(q, i+1, q, i, q->keynum - i);
            q->key[q->keynum] = p->key[order+1];
            q->ptr[q->keynum] = rc->ptr[0];
            p->key[order+1] = rc->key[1];
            rc->ptr[0] = rc->ptr[1];
            LeftMove(rc, 2, rc, 1, rc->keynum-1);
            rc->keynum--;
            break;
        case 4:
            lc = p->ptr[order - 1];
            q->ptr[i] = q->ptr[i-1];
            RightMove(q, i-1, q, i, i-1);
            q->key[1] = p->key[order];
            q->ptr[0] = lc->ptr[lc->keynum];
            p->key[order] = lc->key[lc->keynum];
            lc->keynum--;
            break;
        case 5:
            rc = p->ptr[order + 1];
            LeftMove(q, i+1, q, i, q->keynum - i);
            q->key[q->keynum] = p->key[order+1];
            q->ptr[q->keynum] = rc->ptr[0];
            LeftMove(rc, 1, q, q->keynum + 1, rc->keynum);
            q->keynum += rc->keynum;
            for (int k=0;k<=rc->keynum;k++){
                if (rc->ptr[k]){
                    rc->ptr[k]->parent = p->ptr[order];
                }
            }
            
            delete (p->ptr[order+1]);
            LeftMove(p, order + 2, p, order + 1, p->keynum - order - 1);
            p->keynum--;
            if (p->keynum < s - 1)
            {
                p->keynum++;
                q = p;
                Delete(BT, q, q->keynum);
            }
            break;
        case 6:
            lc = p->ptr[order-1]; //左兄弟结点
            lc->key[lc->keynum+1] = p->key[order]; //双亲结点值下移到左兄弟结点
            lc->ptr[lc->keynum+1] = q->ptr[0];
            LeftMove(q,1,lc,lc->keynum+2,i-1);
            LeftMove(q,i+1,lc,lc->keynum+i+1,q->keynum-i); //将q中删除结点的剩余结点全部拷贝到左兄弟中
            lc->keynum += q->keynum;
            
            for (int k=0;k<=q->keynum;k++){
                if (q->ptr[k]){
                    q->ptr[k]->parent = p->ptr[order-1];
                }
            }
            
            delete (p->ptr[order]); //释放q结点
            LeftMove(p,order+1,p,order,p->keynum-order); //双亲结点中删除下移过后的order
            p->keynum--;
            if (p->keynum<s-1){
                //如果双亲结点现有个数小于最小值则引起连锁反应
                p->keynum++;
                q = p;
                Delete(BT,q,q->keynum); //删除不存在的结点
            }
            break;
    }
}

// 找出BT中最小的关键字
int SearchMinKey(BTree BT, Result &R)
{
    BTree q = BT;
    while (q && q->ptr[0])
        q = q->ptr[0];
    if (q)
    {
        R.pt = q;
        R.tag = 1;
        R.i = 1;
        return 1;
    }
    return 0;
}
// 找出双亲结点: q为p的第order个孩子
int FoundParent(BTree q, BTree &p, int &order)
{
    p = q->parent;
    if (p == NULL)
    {
        order = -1;
        return 0;
    }
    else
    {
        for (order = 0; p->ptr[order] != q; order++);
        return 1;
    }
}

// 向左移动关键字和指针
int LeftMove(BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
    int k;
    if (!old_ptr || !new_ptr || m < 1 || m > old_ptr->keynum )
        return 0;
    for (k = 0; k < len; k++, m++, n++)
    {
        new_ptr->key[n] = old_ptr->key[m];
        new_ptr->ptr[n] = old_ptr->ptr[m];
    }
    return 1;
}

// 向右移动关键字和指针
int RightMove(BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
    int k;
    if (!old_ptr || !new_ptr || m < 1 || m > old_ptr->keynum )
        return 0;
    for (k = 0; k < len; k++, m--, n--)
    {
        new_ptr->key[n] = old_ptr->key[m];
        new_ptr->ptr[n - 1] = old_ptr->ptr[m - 1];
    }
    return 1;
}

// 层序输出B树
/*
 void PrintBT_Level(BTree BT)
 {
 queue<BTNode*> temp;
 temp.push(BT);
 while (!temp.empty())
 {
 BTree T = temp.front();
 temp.pop();
 cout << "<";
 for (int i = 0; i < T->keynum; i++)
 {
 if (T->ptr[i])
 temp.push(T->ptr[i]);
 cout << " " << T->key[i+1];
 }
 cout << ">";
 if (T->ptr[T->keynum])
 temp.push(T->ptr[T->keynum]);
 cout << endl;
 }
 }
 */

void PrintBT_Level(BTree BT)
{
    BTree p[100], q[100];
    int i, j, k;
    int a, b, count;
    
    a = 1;
    p[a] = BT;
    count = 0;
    
    while(a)
    {
        printf("  第 %2d 行关键字:", ++count);
        
        b = 0;
        for(i=1; i<=a; i++)
        {
            printf("(");
            for(j=0; j<=p[i]->keynum; j++)
            {
                if(j)
                    printf(" %2d", p[i]->key[j]);
                
                if(p[i]->ptr[j])
                    q[++b] = p[i]->ptr[j];
            }
            printf(") ");
        }
        
        printf("\n");
        
        a = b;
        for(k=1; k<=b; k++)
            p[k] = q[k];
    }
}

// 中序输出B树
void PrintBT_InOrder(BTree BT)
{
    if (!BT)
        return;
    for (int i = 0; i < BT->keynum; i++)
    {
        PrintBT_InOrder(BT->ptr[i]);
        cout <<"<" << BT->key[i+1] << ">" << endl;
    }
    PrintBT_InOrder(BT->ptr[BT->keynum]);
}

  • main.cpp
//
//  main.cpp
//  BTree
//
//  Created by peiyu wang on 2019/3/25.
//  Copyright © 2019 peiyu wang. All rights reserved.
//
#include "BTree.hpp"

int main(int argc, const char * argv[]) {
    BTree bt = NULL;
    CreateBTree(bt);
    //PrintBT_InOrder(bt);
    PrintBT_Level(bt);
    DeleteKey(bt, 45);
    PrintBT_Level(bt);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值