B树总结(概念、操作及C语言实现)

  最近在复习数据结构,为了加深对B树的理解,整理了如下笔记,然后用C语言实现了一下B树;很久没有使用C语言了,代码有些地方不符合规范或者本文理解有问题,欢迎指正;

一、B树定义

B树,又称多路平衡查找树,B树中所有结点孩子个数的最大值为B树的阶。m阶B树定义如下:空树或者满足如下特征的m叉树:
  1)树中每个结点至多有m棵子树,即至多有m-1个关键字;
  2)若根结点不是终端结点,则至少有2棵子树,即至少一个关键字;
  3)除根节点外所有非叶结点至少有 ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2棵子树,即至少有 ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2-1个关键字;
  4)所有非叶结点结构如下:
  图1 m阶B树结点的描述

图1 B树结点示意图

其中,Ki(i=0,1,2,…,n)为结点的关键字,且满足K1<K2<…<Kn;Pi为指向子树根结点的指针,且指针P i-1所指子树中所有结点关键字均小于K i,P i所指子树中所有结点关键字均大于K i,其中 ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2 - 1 ⩽ \leqslant n ⩽ \leqslant m - 1,表示关键字的个数;
  5)所有空结点出现在同一层次,这些空结点可以视为查找失败的情况;
7阶B树
图2 7阶B树

二、B树的性质:

此处讨论的性质不把外部结点算作叶子结点;
  1)非叶结点孩子个数等于关键字个数加1;
  2)结点中关键字从左至右是递增的,关键字两侧均有指向子树的指针;
   3)B树的高度:对于含n个关键字、高度h、阶数为m的B树
    i)h ⩾ \geqslant logm(n+1)
    B树每个结点最多有m棵子树,m-1个关键字,所以应满足n ⩽ \leqslant (m-1) ( 1 + m + m 2 + m 3 + . . . + m h − 1 ) = m h − 1 ; (1+m+m^2+m^3+...+m^{h-1})=m^h-1; (1+m+m2+m3+...+mh1)=mh1;
    ii)h ⩽ \leqslant log ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2((n+1)/2)+1;
    让每个结点中关键字数目个数达到最少,则B树高度达到最大;此时根结点有一个关键字,有两个子树,剩余所有结点均有 ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2 - 1个关键字,则总关键字个数为1+2*(1+ ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2+ ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2^2+ ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2^3+…+ ⌈ m / 2 ⌉ h − 2 ) ∗ ( ⌈ m / 2 ⌉ − 1 ) = 1 + 2 ∗ ( ⌈ m / 2 ⌉ h − 1 − 1 \lceil m/2 \rceil^{h-2})*(\lceil m/2 \rceil-1)=1+2*(\lceil m/2 \rceil^{h-1} - 1 m/2h2)(m/21)=1+2(m/2h11) = 2* ⌈ m / 2 ⌉ h − 1 \lceil m/2\rceil^{h-1} m/2h1 - 1,所以,n ⩾ \geqslant 2* ⌈ m / 2 ⌉ h − 1 \lceil m/2\rceil^{h-1} m/2h1 - 1;

三、B树的查找

  与二叉树查找类似,每次先从根结点查找多关键字有序表,根据该节点查找结果做多路分支的决定;
  B树查找步骤如下:
   1)工作指针P指向根结点;
   2)比较关键字key与结点P的关键字列表,如果有Ki=key,直接返回此节结点和位置i即可;否则,直到找到Ki < \lt < key < \lt <Ki + 1,令P = P->Pi;
   3)重复上述步骤,直至找到或者P为NULL;若P=NULL,说明没找到;

四、B树的插入

   B树的插入相比于二叉查找树要复杂的多,B树插入后可能会引起某个结点中关键字数目过多,不符合m阶B树的定义,这时会涉及到结点分裂的操作;将关键字插入B树过程如下:
  1)定位:与B树的查找算法,找出插入位置(叶子结点);
  2)在B树中,每个结点关键字个数在区间[ ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2 - 1, m-1]之间,若结点关键字个数 ⩽ \leqslant m-2,则插入关键字后仍然符合B树定义,直接插入即可;否则,要执行如下的结点分裂操作:
   从中间位置( ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2)将插入关键字后的结点进行分裂,位置 ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2处的关键字进位到双亲结点,[0- ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2-1]处的关键字保留在原结点,新生成一个结点存放[ ⌈ m / 2 ⌉ \lceil m/2 \rceil m/2+1,m-1]处的关键字;若进位后引起双亲结点不符合定义,则继续向上进位,直到根节点;如果进位到根节点引起根结点不符合定义,则重新创造一个根节点,只包含进位的关键字;
下面给出两种情况下插入关键字的操作步骤:
在这里插入图片描述

图3 3阶B树插入关键字8

在这里插入图片描述

图4 3阶B树插入关键字36

五、B树的删除

  删除关键字后也可能导致结点不符合B树定义,此时会涉及到结点的合并操作;从B树删除关键字key的过程如下(设被删关键字所在结点为iNode,做兄弟结点为lNode,右兄弟结点为rNode,双亲结点为pNode):
   情形1)iNode不是叶子结点时:用key的前驱或后继key’来代替k,然后删除key’,此时key’必定落在叶结点,则转换为了情形2;
在这里插入图片描述

图5 3阶B树删除80

   情形2)iNode是叶子结点,分三种情况:
    ①iNode关键字个数 ⩾ ⌈ m / 2 ⌉ \geqslant \lceil m/2\rceil m/2,则表明删除key后仍满足B树定义,直接删除即可;
    ②兄弟够借。若iNode关键字个数 < ⌈ m / 2 ⌉ \lt \lceil m/2\rceil <m/2且lNode或rNode关键字个数 ⩾ ⌈ m / 2 ⌉ \geqslant \lceil m/2\rceil m/2,则向lNode借(假设lNode够借); 借用过程如下:
     pNode的指针p i指向iNode,则用K i代替key,然后用lNode的最后一个关键字补上pNode的K i,若lNode的最后一个子结点不空,则将此子结点插入iNode;
    ③兄弟不够借。若iNode、lNode、rNode关键字个数均 < ⌈ m / 2 ⌉ \lt \lceil m/2\rceil <m/2,则将关键字删除后的iNode和lNode或者rNode合并;合并时,双亲结点关键字个数-1,若因此导致双亲结点pNode不符合B数定义(pNode为根节点时,pNode的关键字个数 > \gt > 1即可),则pNode要和其左右兄弟结点进行调整(借或合并),重复上述步骤,直至符合B树要求; 合并过程如下:
     i)若pNode为根结点且关键字个数为1,则将pNode的关键字和子结点加上iNode剩余的信息并入rNode(假设rNode存在),并令rNode为B树根节点;
     ii)若pNode不是根,pNode的指针pi指向iNode,则把pNode的关键字K i和iNode剩余信息并入rNode,然后删除iNode;
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
图6 7阶B树删除关键字22

六、C语言实现

项目结构如下:
在这里插入图片描述1、BTree.h:

#pragma once
#define MAX_ORDER 6 // B树最大阶数
#define TREE_ORDER 4 // B数阶数
#define MIN_KEY_NUM (TREE_ORDER - 1)/2 // 非根结点最小关键字数目
#define MAX_KEY_NUM TREE_ORDER - 1 // 每个结点最大关键字数目
#define ROOT_MIN_KEY_NUM 1 // 根结点最大关键字数目
#define CEIL(x) (x+1)/2 // 向上取整
// 因为此B树每个结点数据是从下标0开始存储的,所以-1
#define ROOT_INDEX (TREE_ORDER - 1)/2 //结点分裂时需要进位的数据下标
#define NOT_FOUND -1 // 未找到标志

// 关键字类型
typedef int KeyType;
// 结点数据类型
typedef struct MyType {
     KeyType key;
}DataType;

// BTree操作的状态
enum Status{
     OK,
     ERROR
};

// 查找状态标志
enum Tag{
     SUCCESS, //查找成功
     FAIL, // 查找失败
     INSERT, // 查找插入位置
     DELETE
};

// BlanceTree结点
typedef struct BTreeNode{
     int keyNum;
     int childNum;
     BTreeNode *parent;
     BTreeNode *child[TREE_ORDER];
     DataType data[MAX_KEY_NUM];
}BTNode, *BTree;

// BTree查找结果
typedef struct {
     BTNode *node; // 查找的结点
     int pos; // 结点中关键字的位置
     Tag tag; // 是否找到
}Result;

// 初始化树
Status InitBTree(BTree &T);

/* 初始化树
     dataList:初始数据列表
*/
Status InitBTree(BTree &T,  DataType *dataList, int dataNum);

// 把数据插入树
Status InsertData(BTree &T, DataType data);

// 根据关键字寻找数据插入位置
Result SearchInsertPos(BTree T, KeyType key);

/* 把数据加入某个结点
     T:此结点所属的树
     inPos:插入位置信息
     data:需要插入的数据
     subNewNode:子结点因数据过多分裂产生的新结点
*/
Status DataJoinNode(BTree &T, Result inPos, DataType data, BTNode* &subNewNode);

// 根据关键字查找数据
DataType SearchDataByKey(BTree T, KeyType key);

// 根据关键字查找数据的位置
Result SearchData(BTree T, KeyType key);

// 根据数据位置删除数据
Status DeleteData(BTree &T, Result dPos);

/* 在树T中合并父节点,当前结点(iNode)和其左右兄弟结点
     T:树
     pNode:iNode的双亲结点
     lNode:iNode的左兄弟结点
     iIndex:iNode在pNode中下标
     rNode:iNode的右兄弟结点
*/
Status MergeNode(BTree &T, BTNode* pNode, BTNode* lNode, int iIndex, BTNode* rNode);

// 找到以rootNode为根的树的最后一个结点
BTNode* FindLastNode(BTNode* rootNode);

// 根据关键字删除数据
Status DeleteData(BTree &T, KeyType key);

// 按升序输出BTree的关键字
void PrintBTree(BTree T);

2、BTree.cpp

#include<iostream>
#include"BTree.h"
using namespace std;

Status InitNewNode(BTNode* &newNode) {
     newNode = new BTNode;
     newNode->keyNum = 0;
     newNode->childNum = 0;
     newNode->parent = NULL;
     for (int i = 0; i < TREE_ORDER; i++) {
          newNode->child[i] = NULL;
     }
     return OK;
}

Status InitBTree(BTree &T) {
     if (TREE_ORDER > MAX_ORDER) {
          cout << "B树阶数过大" << endl;
          return ERROR;
     }
     else {
          T = new BTNode;
          T->keyNum = 0;
          T->childNum = 0;
          T->parent = NULL;
          for (int i = 0; i < TREE_ORDER; i++) {
               T->child[i] = NULL;
          }
          return OK;
     }
}
Status InitBTree(BTree &T,  DataType *dataList, int dataNum) {
     if (InitBTree(T) == OK) {
          for (int i = 0; i < dataNum; i++)
               if (InsertData(T, dataList[i]) == ERROR)
                    return ERROR;
          return OK;
     }
     return ERROR;
}

Status InsertData(BTree &T, DataType data) {
     Result inPos = SearchInsertPos(T, data.key);
     BTNode* subNewNode = NULL;
     return DataJoinNode(T, inPos, data, subNewNode);
}

Result SearchInsertPos(BTree T, KeyType key) {
     BTNode *p = NULL, *q = T;
     int i = 0;
     while (q) {
          for (i = 0; i < q->keyNum && q->data[i].key < key; i++);
          p = q;
          q = q->child[i];
     }
     return {p, i, INSERT};
}

Status DataJoinNode(BTree &T, Result inPos, DataType data, BTNode* &subNewNode) {
     BTNode *iNode = inPos.node;
     int i = 0,k = 0;
     if (iNode->keyNum < MAX_KEY_NUM) {// 情形1:直接插入
          for (i = iNode->keyNum; i > inPos.pos; i--) {
               iNode->data[i] = iNode->data[i - 1];
               iNode->child[i + 1] = iNode->child[i];
          }
          iNode->data[i] = data;
          iNode->keyNum += 1;
          if (subNewNode) {
               iNode->child[i + 1] = subNewNode;
               subNewNode->parent = iNode;
               iNode->childNum += 1;
          }
     }else { // 情形2:结点分裂
          // 临时数组,保存插入关键字后此结点的数据
          DataType temp[TREE_ORDER];
          BTNode* tempNodes[TREE_ORDER + 1];
          for (i = 0; i < inPos.pos; i++) {
               temp[i] = iNode->data[i];
               tempNodes[i] = iNode->child[i];
          }
          tempNodes[i] = iNode->child[i];
          tempNodes[i + 1] = subNewNode;
          temp[i] = data;
          for (; i < iNode->keyNum; i++) {
               temp[i + 1] = iNode->data[i];
               tempNodes[i + 2] = iNode->child[i + 1];
          }

          /* 结点分裂
               iNode保存左边信息,newNode保存右边信息
          */
          iNode->childNum = 0;
          for (i = 0; i < TREE_ORDER; i++) {
               if (tempNodes[i] && i <= ROOT_INDEX) {
                    iNode->child[i] = tempNodes[i];
                    iNode->childNum++;
               }
               else {
                    iNode->child[i] = NULL;
               }
          }
          iNode->keyNum = ROOT_INDEX;

          // 创造一个新的结点
          BTNode* newNode;
          InitNewNode(newNode);
          newNode->keyNum = MAX_KEY_NUM - ROOT_INDEX;
          for (i = ROOT_INDEX + 1, k = 0; i <= TREE_ORDER; i++,k++) {
               newNode->data[k] = temp[i];
               if (tempNodes[i]) {
                    newNode->child[k] = tempNodes[i];
                    newNode->childNum += 1;
                    tempNodes[i]->parent = newNode;
               }
          }

          BTNode *pNode = iNode->parent; //当前结点的父节点
          if (pNode) { // 父节点不空,可以继续向上层进位
               for (i = 0; i < pNode->keyNum && pNode->data[i].key < temp[ROOT_INDEX].key; i++);
               Result pInPos = { pNode, i, INSERT };
               DataJoinNode(T, pInPos, temp[ROOT_INDEX], newNode);
          }else { // 父节点空,即当前结点是根,重新生成根节点
               BTNode* rootNode;
               InitNewNode(rootNode);
               rootNode->childNum = 2;
               rootNode->keyNum = 1;
               rootNode->child[0] = iNode;
               rootNode->child[1] = newNode;
               rootNode->data[0] = temp[ROOT_INDEX];

               newNode->parent = rootNode;
               iNode->parent = rootNode;

               T = rootNode;
          }
     }

     return OK;
}

DataType SearchDataByKey(BTree T, KeyType key) {
     BTNode* p = T;
     int i;
     while (p) {
          for (i = 0; i < p->keyNum; i++) {
               if (p->data[i].key == key)
                    return p->data[i];
               else if (p->data[i].key > key)
                    break;
          }
          p = p->child[i];
     }
     return { {NOT_FOUND} };
}

Result SearchData(BTree T, KeyType key) {
     BTNode* p = T;
     int i;
     while (p) {
          for (i = 0; i < p->keyNum; i++) {
               if (p->data[i].key == key)
                    return { p, i, SUCCESS };
               else if (p->data[i].key > key)
                    break;
          }
          p = p->child[i];
     }
     return { NULL, -1, FAIL };
}

Status MergeNode(BTree &T, BTNode* pNode, BTNode* lNode, int iIndex, BTNode* rNode) {
     if (!pNode || !T || iIndex < 0 || !rNode && !lNode)
          return ERROR;
     int i = 0;
     BTNode* iNode = pNode->child[iIndex];
     // 假设pNode->child[iIndex]指向iNode
     if (rNode && rNode->keyNum > MIN_KEY_NUM) {
          /* 如果rNode借位后关键字数目大于阈值
               从rNode借位给iNode:假设pNode->child[i]指向iNode
          */

          // (1)把pNode->data[iIndex]加入iNode;
          iNode->data[iNode->keyNum++] = pNode->data[iIndex];
          // (2)把rNode->data[0]代替pNode->data[iIndex]
          pNode->data[iIndex] = rNode->data[0];
          // (3)将rNode->child[0]并入iNode
          if (rNode->child[0])
               rNode->child[0]->parent = iNode;
          iNode->child[iNode->childNum++] = rNode->child[0];
          // (4)从rNode中删除data[0]和child[0]
          for (i = 0; i < rNode->keyNum - 1; i++) {
               rNode->data[i] = rNode->data[i + 1];
          }
          rNode->keyNum--;
          if (rNode->childNum > 0) {
               for (i = 0; i < rNode->childNum - 1; i++) {
                    rNode->child[i] = rNode->child[i + 1];
               }
               rNode->child[--rNode->childNum] = NULL;
          }
     }
     else if (lNode && lNode->keyNum > MIN_KEY_NUM) {
          /* 如果lNode借位后关键字数目大于阈值
               从lNode借位给iNode:
          */
          // (1)把pNode->data[iIndex - 1]加入iNode;
          for (i = iNode->keyNum; i > 0; i--)
               iNode->data[i] = iNode->data[i - 1];
          iNode->data[0] = pNode->data[iIndex - 1];
          iNode->keyNum++;
          // (2)把lNode->data[end]代替pNode->data[iIndex - 1]并删除lNode->data[end]
          pNode->data[iIndex - 1] = lNode->data[--lNode->keyNum];
          // (3)将lNode->child[end]并入iNode并删除
          if (iNode->childNum > 0) {
               for (i = iNode->childNum; i > 0; i++)
                    iNode->child[i] = iNode->child[i - 1];
               iNode->child[0] = lNode->child[--lNode->childNum];
               iNode->child[0]->parent = iNode;
               lNode->child[lNode->childNum] = NULL;
          }
     } else if (!pNode->parent && pNode->keyNum <= ROOT_MIN_KEY_NUM) { // pNode是根结点并且借位后关键字数目小于阈值
          if (rNode && rNode->keyNum == MIN_KEY_NUM) { 
               /*
                    1、若rNode存在,则把iNode剩余信息和根结点信息合并至rNode;
                    2、令rNode为根节点
               */

               // 将根结点和iNode剩余关键字并入rNode
               for (i = rNode->keyNum - 1; i >= 0; i++)
                    rNode->data[i + MIN_KEY_NUM] = rNode->data[i];
               for (i = 0; i < iNode->keyNum; i++) {
                    rNode->data[i] = iNode->data[i];
                    rNode->keyNum++;
               }
               rNode->data[i] = pNode->data[0];
               rNode->keyNum++;

               // 将iNode剩余的子结点挪至rNode
               for (i = rNode->childNum - 1; i >= 0; i++)
                    rNode->child[i + MIN_KEY_NUM] = rNode->child[i];
               for (i = 0; i < iNode->childNum && iNode->child[i]; i++) {
                    iNode->child[i]->parent = rNode; // 修改iNode子结点的双亲指针
                    rNode->child[i] = iNode->child[i];
                    rNode->childNum++;
               }
               delete iNode; 
               T = rNode; 
          }
          else if (lNode && lNode->keyNum == MIN_KEY_NUM) {
               /*
                   1、若lNode存在,则把iNode剩余信息和根结点信息合并至lNode;
                   2、令lNode为根节点
              */

              // 将根结点和iNode剩余关键字并入lNode
               lNode->data[lNode->keyNum++] = pNode->data[0];
               for (i = 0; i < iNode->keyNum; i++)
                    lNode->data[lNode->keyNum++] = iNode->data[i];
               // 将iNode剩余子结点并入lNode
               for (i = 0; i < iNode->childNum && iNode->child[i]; i++) {
                    iNode->child[i]->parent = lNode;
                    lNode->child[lNode->childNum++] = iNode->child[i];
               }
               delete iNode;
               T = lNode;
          }
     } else {
          if (lNode) { 
               /*
                    lNode存在,从pNode借一位加上iNode剩余信息并入lNode
               */

               //(1)pNode->data[iIndex - 1]和iNode剩余关键字并入lNode
               lNode->data[lNode->keyNum++] = pNode->data[iIndex - 1];
               for (i = 0; i < iNode->keyNum; i++)
                    lNode->data[lNode->keyNum++] = iNode->data[i];
               for (i = iIndex - 1; i < pNode->keyNum - 1; i++)
                    pNode->data[i] = pNode->data[i + 1];
               pNode->keyNum--;
               //(2)如果iNode有子结点,并入lNode
               if (lNode->childNum > 0) {
                    for (i = 0; i < iNode->childNum; i++) {
                         iNode->child[i]->parent = lNode;
                         lNode->child[lNode->childNum++] = iNode->child[i];
                    }
                    
               }
               for (i = iIndex; i < pNode->childNum - 1; i++)
                    pNode->child[i] = pNode->child[i + 1];
               pNode->child[--pNode->childNum] = NULL;
               delete iNode;
               
          }else {
               /*
                   rNode存在,从pNode借一位加上iNode剩余信息并入rNode
              */

               //(1)pNode->data[iIndex]和iNode剩余关键字并入rNode
               for (i = rNode->keyNum - 1; i >= 0; i--)
                    rNode->data[i + MIN_KEY_NUM] = rNode->data[i];
               rNode->data[i + MIN_KEY_NUM] = pNode->data[iIndex];
               rNode->keyNum++;
               for (i = 0; i < iNode->keyNum; i++) {
                    rNode->data[i] = iNode->data[i];
                    rNode->keyNum++;
               }
               for (i = iIndex; i < pNode->keyNum - 1; i++)
                    pNode->data[i] = pNode->data[i + 1];
               pNode->keyNum--;
               // (2)如果iNode有子结点,并入rNode
               if (iNode->childNum > 0) {
                    for (i = rNode->childNum - 1; i >= 0; i--)
                         rNode->child[i + MIN_KEY_NUM] = rNode->child[i];
                    for (i = 0; i < iNode->childNum; i++) {
                         iNode->child[i]->parent = rNode;
                         rNode->child[i] = iNode->child[i];
                         rNode->childNum++;
                    }
               }

               for (i = iIndex; i < pNode->childNum - 1; i++)
                    pNode->child[i] = pNode->child[i + 1];
               pNode->child[--pNode->childNum] = NULL;
               delete iNode;
          }

          if (pNode->parent && pNode->keyNum < MIN_KEY_NUM) {
               // 当pNode不是根节点且关键字数目小于阈值,将其双亲结点、左右兄弟结点合并
               BTNode* ppNode = pNode->parent;
               BTNode *plNode = NULL, *prNode = NULL;
               for (i = 0; i < ppNode->childNum; i++)
                    if (ppNode->child[i] == pNode)
                         break;
               if (i >= 1)
                    plNode = ppNode->child[i - 1];
               if (i <= ppNode->childNum - 1)
                    prNode = ppNode->child[i + 1];
               MergeNode(T, ppNode, plNode, i, prNode);
          }
     }
     return OK;
}

Status DeleteData(BTree &T, Result dPos) {
     int pos = dPos.pos;
     BTNode* node = dPos.node;
     if (node == NULL || pos < 0) {
          cout << "删除出错" << endl;
          return ERROR;
     }else {
          if (node->childNum > 0) {
               // node是非终端结点,用其前驱代替之,然后删除前驱位置的关键字
               BTNode* preNode = FindLastNode(node->child[pos]);
               node->data[pos] = preNode->data[preNode->keyNum - 1];
               DeleteData(T, { preNode, preNode->keyNum - 1, DELETE});
          }else {
               if (node->keyNum > MIN_KEY_NUM) { // 关键字数目大于阈值,直接删除
                    node->keyNum -= 1;
               }else { // 关键字数目小于阈值,删除后合并
                    for (int i = pos; i < node->keyNum - 1; i++)
                         node->data[i] = node->data[i + 1];
                    node->keyNum--;
                    BTNode* pNode = node->parent;
                    if (pNode) { // node不是根节点,需要合并双亲结点、左右兄弟结点
                         BTNode *lNode = NULL, *rNode = NULL;
                         int iIndex = 0;
                         for (; iIndex < pNode->childNum; iIndex++) {
                              if (pNode->child[iIndex] == node)
                                   break;
                         }
                         if (iIndex >= 1)
                              lNode = pNode->child[iIndex - 1];
                         if (iIndex <= pNode->childNum - 1)
                              rNode = pNode->child[iIndex + 1];
                         MergeNode(T, pNode, lNode, iIndex, rNode);
                    }
               }
          }
     }
     return OK;

}

Status DeleteData(BTree &T, KeyType key) {
     Result res = SearchData(T, key);
     if (res.tag == FAIL)
          return ERROR;
     else {
          res.tag = DELETE;
          DeleteData(T, res);
     }

     return OK;
}

BTNode* FindLastNode(BTNode* rootNode) {
     BTNode* p = rootNode;

     while (p->childNum > 0) { 
          p = p->child[p->childNum - 1];
     }
     return p;
}

void PrintBTree(BTree T) {
     BTNode* p = T;
     if (p->childNum == 0) {
          for (int i = 0; i < p->keyNum; i++)
               cout << p->data[i].key << ",";
     }
     else {
          int i = 0;
          for (; i < p->keyNum; i++) {
               PrintBTree(p->child[i]);
               cout<< p->data[i].key << ",";
          }
          PrintBTree(p->child[i]);
     }
}

3、main.cpp

#include<iostream>
#include"BTree.h"
using namespace std;
int main() {
     DataType dataList[15] = { {89}, {92}, {1},{3},{5},{6},{8},{9},{11},{13},{15},{22},{30},{35},{36} };
     BTree tree;
     InitBTree(tree, dataList, 15);
     cout << "B树初始化完成后遍历结果:";
     PrintBTree(tree);
     cout << endl;
     DeleteData(tree, 3);
     cout << "删除关键字3后遍历结果:";
     PrintBTree(tree);
     cout << endl;
     system("pause");
     return 0;
}

4、运行结果:
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值