数据结构-动态查找表(B树的基本操作代码实现)

部分思路借鉴:(209条消息) C语言-B树(B-树)的完整实现_geek_jerome的博客-CSDN博客_b树实现icon-default.png?t=M0H8https://blog.csdn.net/geek_jerome/article/details/78895289?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164480219116781683942244%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164480219116781683942244&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-78895289.pc_search_result_cache&utm_term=B%E6%A0%91%E4%BB%A3%E7%A0%81&spm=1018.2226.3001.4187 

#include <iostream>
using namespace std;


const int m = 3;//设定B树的阶数
//关键字的个数n必须满足:[m/2]-1≤n≤m-1
const int MaxKeyNum = m - 1;//最大关键字数量
const int MinKeyNum = m / 2 - 1;//最小关键字数量
typedef int ElemType;//关键字类型定义
typedef struct BTNode {
    int keynum; //结点中关键字的个数
    ElemType key[m + 1]; //关键字数组,0号单元未用
    struct BTNode* parent;//双亲结点指针
    struct BTNode* ptr[m + 1]; //子树结点指针数组
}BTNode, * BTree;
typedef struct {
    BTNode* pt; //指向找到的结点
    int i; //在结点中的关键字位置
    int tag; //查找是否成功的标志
}Result; //查找结果类型

//队列---------------------------------------
typedef BTNode* QElemType;
typedef struct QNode {
    QElemType data;
    struct QNode* next;
}QNode, * QueuePtr;
typedef struct {
    QueuePtr front;
    QueuePtr rear;
}LinkQueue;

bool InitQueue(LinkQueue& Q);//初始化链队列
bool EnQueue(LinkQueue& Q, QElemType e);//将元素e入队
bool DeQueue(LinkQueue& Q, QElemType& e);//将队首元素出队,并用元素e返回
bool DestroyQueue(LinkQueue& Q);//销毁队列
int QueueLength(LinkQueue Q);//返回队列长度

bool InitQueue(LinkQueue& Q) {
    Q.front = Q.rear = new QNode;//头节点
    if (!Q.front || !Q.rear)return 0;
    Q.front->next = NULL;
    return 1;
}
bool EnQueue(LinkQueue& Q, QElemType e) {
    QueuePtr temp = new QNode;
    if (!temp)return 0;
    temp->data = e;
    temp->next = NULL;
    Q.rear->next = temp;
    Q.rear = temp;
    return 1;
}
bool DeQueue(LinkQueue& Q, QElemType& e) {
    if (Q.front == Q.rear)
        return 0;
    QueuePtr temp = Q.front->next;//指向第一个结点的指针
    e = temp->data;
    Q.front->next = temp->next;
    if (Q.rear == temp)//若出队的是尾指针指向结点,则需要调整尾指针,否则删除后尾指针指向空
        Q.rear = Q.front;
    delete temp;
    return 1;
}
bool DestroyQueue(LinkQueue& Q) {
    QueuePtr temp = Q.front;
    while (Q.front) {
        Q.front = Q.front->next;
        delete temp;
        temp = Q.front;
    }
    return 1;
}
int QueueLength(LinkQueue Q) {
    int n = 0;
    while (Q.front != Q.rear) {
        n++;
        Q.front = Q.front->next;
    }
    return n;
}


//B树---------------------------------------
void CreateBTree(BTree& T);//创建B树
void InsertBTNode(BTNode*& p, int i, ElemType e, BTNode* q);//将关键字e和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void InsertBTree(BTree& T, int i, ElemType e, BTNode* p);//将关键字e和新建结点去q分别插入到p->key[i+1]和p->ptr[i+1]中,加入分裂机制
int SearchBTNode(BTNode* p, ElemType e);//返回结点p中元素e的应插入位置
void SearchBTree(BTree T, ElemType e, Result& s);//在B树中查找元素e,将查找结果用s返回,
void CreateNewNode(BTNode*& T, ElemType e, BTNode* p, BTNode* q);//p和q是子树指针,因为m-1等于2,所以有两个
void SplitBTNode(BTNode*& p, BTNode*& q);//将结点p分裂成两个结点,前一半保留,后一半移入结点q,最中间位置的点升为双亲结点
void Traverse(BTree T, LinkQueue L, int newline = 0, int sum = 0);//遍历
bool DestroyBTree(BTree& T);//销毁B树
int FindBTNode(BTNode* p, ElemType e, int& i);//判断元素e是否存在于结点p中,用i返回位次
void Substitution(BTNode* p, int i);//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void Remove(BTNode* p, int i);//删除p结点的key[i+1]和ptr[i]
void AdjustBTree(BTNode* p, int i);//删除之后需要调整B树
void MoveRight(BTNode* p, int i);
void MoveLeft(BTNode* p, int i);
void Combine(BTNode* p, int i);
int BTreeDelete(BTree p, ElemType e);//删除B树中的元素e,删除失败返回0



void CreateBTree(BTree& T) {
    Result s;
    s.i = 0; s.pt = NULL; s.tag = 0;
    cout << "即将建立" << m << "阶B树:" << endl;
    T = NULL;//初始化B树为空
    cout << "输入关键字个数:" << endl;
    int n;  cin >> n;
    cout << "输入结点关键字值:" << endl;
    ElemType e;
    for (int i = 0; i < n; i++) {
        cin >> e;
        SearchBTree(T, e, s);
        if (s.tag == 0) //证明元素e未在B树中,可以插入
            InsertBTree(T, s.i, e, s.pt);
    }
}
int SearchBTNode(BTNode* p, ElemType e) {
    int num = 0;
    while (num < p->keynum && p->key[num + 1] <= e)  
        num++;//循环结束后第num+1位大于e,第num位小于等于e
    return num;
}
void SearchBTree(BTree T, ElemType e, Result& s) {
    bool tag = 0;//标记是否找到元素e
    int i = 0;//用来接收元素的应插入位次
    BTNode* p = T, * q = NULL;//初始化结点p指向正在查询的结点,结点q指向p的双亲
    while (p != NULL && tag != 1) {//除非查到底至空,或者已经查到,否则一直循环下去
        i = SearchBTNode(p, e);
        if (p->key[i] == e && i > 0)//key的首地址不使用,因此i是从1开始的
            tag = 1;//找到
        else {
            q = p;
            p = p->ptr[i];//应插入位置非所查元素就继续按此路线查下去
        }
    }
    if (tag == 1) {//查找成功,返回当前结点
        s.pt = p;   
        s.i = i;
        s.tag = 1;
    }
    else {//查找失败,返回最后查到的结点
        s.pt = q;
        s.i = i;
        s.tag = 0;
    }
}
void InsertBTNode(BTNode*& p, int i, ElemType e, BTNode* q) {
    int j;
    for (j = p->keynum; j > i; j--) {//整体后移空出一个位置
        p->key[j + 1] = p->key[j];
        p->ptr[j + 1] = p->ptr[j];
    }
    p->key[i + 1] = e;
    p->ptr[i + 1] = q;
    if (q != NULL)
        q->parent = p;
    p->keynum++;
}
void InsertBTree(BTree& T, int i, ElemType e, BTNode* p) {
    BTNode* q;
    bool finish_tag = 0, newnode_tag = 0;//标志是否插入完成,标志是否需要新结点
    if (p == NULL)//若传进来的应插入结点为空,则证明T是空树,直接插入根节点
        CreateNewNode(T, e, NULL, NULL);//子树为空
    else {
        q = NULL;
        while (finish_tag == 0 && newnode_tag == 0) {//不需要新结点,查找没有结束
            InsertBTNode(p, i, e, q);//将关键字e和结点q分别插入到p->key[i+1]和p->ptr[i+1]
            if (p->keynum <= MaxKeyNum)
                finish_tag = 1;//添加结点后并未超出范围
            else {//添加结点后超出范围
                int s = (m + 1) / 2;//中间位置
                SplitBTNode(p, q);//分裂结点 
                e = p->key[s];//废物利用,让e负责接收中间位置的关键值
                if (p->parent) {//查找接收中间值后的e的插入位置
                    p = p->parent;
                    i = SearchBTNode(p, e);
                }
                else//没找到e,需要新结点 
                    newnode_tag = 1;
            }
        }
        if (newnode_tag == 1)//根结点已分裂为结点p和q 
            CreateNewNode(T, e, p, q);//生成新根结点t,p和q为子树指针
    }
}
void SplitBTNode(BTNode*& p, BTNode*& q) {
    //将结点p分裂成两个结点,前一半保留,后一半移入结点q
    int i;
    int s = (m + 1) / 2;//中间位置
    q = new BTNode;//给结点q分配空间

    q->ptr[0] = p->ptr[s];//后一半移入结点q
    for (i = s + 1; i <= m; i++) {
        q->key[i - s] = p->key[i];//key是从1开始计算的
        q->ptr[i - s] = p->ptr[i];
    }
    q->keynum = p->keynum - s;
    q->parent = p->parent;//分裂后还是兄弟结点
    for (i = 0; i <= q->keynum; i++)//修改双亲指针,从p改为q
        if (q->ptr[i] != NULL)
            q->ptr[i]->parent = q;
    p->keynum = s - 1;//结点p的前一半保留,中间位置的s升级为双亲结点
}
void CreateNewNode(BTNode*& T, ElemType e, BTNode* p, BTNode* q) {
    T = new BTNode;//分配空间 
    T->keynum = 1;
    T->ptr[0] = p;
    T->ptr[1] = q;
    T->key[1] = e;
    if (p != NULL)//若子树结点为空,则不需要调整子树的双亲结点
        p->parent = T;
    if (q != NULL)
        q->parent = T;
    T->parent = NULL;
}
void Traverse(BTree T, LinkQueue L, int newline, int sum) {
    BTree p;
    if (T != NULL) {
        cout << "  [ ";
        EnQueue(L, T->ptr[0]);//入队
        for (int i = 1; i <= T->keynum; i++) {
            cout << " " << T->key[i] << " ";
            EnQueue(L, T->ptr[i]);//子结点入队 
        }
        sum += T->keynum + 1;//keynum个关键字有keynum+1个空当存指针
        cout << "]";
        if (newline == 0) {//需要另起一行 
            cout << endl;
            newline = sum - 1;
            sum = 0;
        }
        else
            newline--;
    }

    if (QueueLength(L) != 0) {//l不为空 
        DeQueue(L, p);//出队,以p返回 
        Traverse(p, L, newline, sum); //遍历出队结点 
    }
}
bool DestroyBTree(BTree& T) {
    if (!T) { //B树不为空  
        for (int i = 0; i <= T->keynum; i++)//递归释放每一个结点 
            DestroyBTree(T->ptr[i]);
        delete T;
    }
    T = NULL;
    return 1;
}
int FindBTNode(BTNode* p, ElemType e, int& i) {
    if(e < p->key[1]) {//结点p中查找关键字k失败 
        i = 0;
        return 0;
    }
    else {//在p结点中查找
        i = p->keynum;
        while (e < p->key[i] && i>1)
            i--;
        if (e == p->key[i])//结点p中查找关键字k成功 
            return 1;
    }
    return 0;
}
void Substitution(BTNode* p, int i) {
    //查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字) 
    BTNode* q;
    for (q = p->ptr[i]; q->ptr[0] != NULL; q = q->ptr[0]);//左边小
    p->key[i] = q->key[1];                            //复制关键字值
}
void Remove(BTNode* p, int i) {
    //从p结点删除key[i]和它的孩子指针ptr[i]
    int j;
    for (j = i + 1; j <= p->keynum; j++) {//前移删除key[i]和ptr[i],ptr[0]是最左侧不用删,必须留着!!!!
        p->key[j - 1] = p->key[j];
        p->ptr[j - 1] = p->ptr[j];
    }
    p->keynum--;
}
void MoveRight(BTNode* p, int i) {
    /*将双亲结点p中的最后i个关键字移入右结点last_q中
    将左结点first_q中的最后i个关键字移入双亲结点p中*/
    BTNode* last_q = p->ptr[i];
    BTNode* first_q = p->ptr[i - 1];
    for (int j = last_q->keynum; j > 0; j--) {//将右兄弟last_q中所有关键字向后移动一位
        last_q->key[j + 1] = first_q->key[j];
        last_q->ptr[j + 1] = first_q->ptr[j];
    }
    last_q->ptr[1] = last_q->ptr[0];//从双亲结点p移动关键字到右兄弟last_q中,原来的最左侧孩子现在是第二个,现在的最左侧是p带过来的
    last_q->key[1] = p->key[i];
    last_q->keynum++;

    p->key[i] = first_q->key[first_q->keynum];//将左兄弟first_q中最后一个关键字移动到双亲结点p中
    p->ptr[i]->ptr[0] = first_q->ptr[first_q->keynum];//这个是first_q最后一个关键字左边的孩子结点,带到了p中
    first_q->keynum--;
}
void MoveLeft(BTNode* p, int i) {
    /*将双亲结点p中的第i个关键字移入左结点first_q中,
    将右结点q中的第i个关键字移入双亲结点p中*/
    BTNode* first_q = p->ptr[i - 1];
    BTNode* last_q = p->ptr[i];
    first_q->keynum++;//把双亲结点p中的关键字移动到左兄弟first_q中
    first_q->key[first_q->keynum] = p->key[i];
    first_q->ptr[first_q->keynum] = p->ptr[i]->ptr[0];

    p->key[i] = last_q->key[1];//把右兄弟last_q中的关键字移动到双亲节点p中
    last_q->ptr[0] = last_q->ptr[1];
    last_q->keynum--;

    for (int j = 1; j <= last_q->keynum; j++) {//将右兄弟last_q中所有关键字向前移动一位
        last_q->key[j] = last_q->key[j + 1];
        last_q->ptr[j] = last_q->ptr[j + 1];
    }
}
void Combine(BTNode* p, int i) {
    /*将双亲结点p、右结点q合并入左结点aq,
    并调整双亲结点p中的剩余关键字的位置*/
    int j;
    BTNode* q = p->ptr[i];
    BTNode* aq = p->ptr[i - 1];

    aq->keynum++;//将双亲结点的关键字p->key[i]插入到左结点aq     
    aq->key[aq->keynum] = p->key[i];
    aq->ptr[aq->keynum] = q->ptr[0];

    for (j = 1; j <= q->keynum; j++) {//将右结点q中的所有关键字插入到左结点aq 
        aq->keynum++;
        aq->key[aq->keynum] = q->key[j];
        aq->ptr[aq->keynum] = q->ptr[j];
    }

    for (j = i; j < p->keynum; j++) {//将双亲结点p中的p->key[i]后的所有关键字向前移动一位 
        p->key[j] = p->key[j + 1];
        p->ptr[j] = p->ptr[j + 1];
    }
    p->keynum--;//修改双亲结点p的keynum值 
    delete q;//释放空右结点q的空间
}
void AdjustBTree(BTNode* p, int i) {
    //删除结点p中的第i个关键字后,调整B树
    if (i == 0)                                        //删除的是最左边关键字
        if (p->ptr[1]->keynum > MinKeyNum)                   //右结点可以借
            MoveLeft(p, 1);
        else                                        //右兄弟不够借 
            Combine(p, 1);
    else if (i == p->keynum)                           //删除的是最右边关键字
        if (p->ptr[i - 1]->keynum > MinKeyNum)                 //左结点可以借 
            MoveRight(p, i);
        else                                        //左结点不够借 
            Combine(p, i);
    else if (p->ptr[i - 1]->keynum > MinKeyNum)                //删除关键字在中部且左结点够借 
        MoveRight(p, i);
    else if (p->ptr[i + 1]->keynum > MinKeyNum)                //删除关键字在中部且右结点够借 
        MoveLeft(p, i + 1);
    else                                            //删除关键字在中部且左右结点都不够借
        Combine(p, i);
}
int BTreeDelete(BTNode* p, ElemType e) {
    int i;
    int found_tag;//查找标志 
    if (p == NULL)
        return 0;
    else {
        found_tag = FindBTNode(p, e, i);//返回查找结果 
        if (found_tag == 1) {//查找成功 ,i从1开始
            if (p->ptr[i - 1] != NULL) {//删除的是非叶子结点
                Substitution(p, i);//寻找相邻关键字(右子树中最小的关键字) 
                BTreeDelete(p->ptr[i], p->key[i]);//执行删除操作 
            }
            else
                Remove(p, i);//叶子结点直接删,不用考虑找代替
        }
        else
            found_tag = BTreeDelete(p->ptr[i], e);//沿孩子结点递归查找并删除关键字e
        if (p->ptr[i] != NULL)
            if (p->ptr[i]->keynum < MinKeyNum)//删除后关键字个数小于MIN
                AdjustBTree(p, i);//调整B树 
        return found_tag;
    }
}



int main()
{
    Result s;
    s.i = 0; s.pt = NULL; s.tag = 0;
    BTree T; LinkQueue L; InitQueue(L);
    CreateBTree(T);
    cout << "请输入要查找的关键值:" << endl;
    ElemType e;
    cin >> e;
    SearchBTree(T, e, s);
    if (s.tag == 1)
        cout << e << "找到了!" << endl;
    else
        cout << "没找到!" << endl;
    Traverse(T, L);
    cout << "请输入要删除的元素:" << endl;
    ElemType ee;
    cin >> ee;
    if (!BTreeDelete(T, ee))
        cout << "删除元素不存在!" << endl;
    else {
        cout << "删除后遍历:" << endl;
        Traverse(T, L);
    }
    if (DestroyBTree(T))
        cout << "销毁成功!" << endl;
}

实验样例

 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜菜的大鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值