算法导论B树的c++实现

    算法导论的课要完成一个小课题,选了B树,花了挺长时间弄完,之前写了几个简单的排序,发现这递归是真的牛批啊。代码部分参考了这位博主。在写前面的插入那一大块,书上有伪代码可以对着写。但是后面的删除部分没有伪代码,懵了好久,找到另一位博主写的伪代码,有几处小错误,我重新写了一遍,并加上了注释(自己理解的注释,可能有地方理解有误)。不多bb,直接上伪代码和最终源码


// 伪代码均以书上的标准对照着写的,下标均以1开始

 

//  x表示父结点,i表示x中要向下合并的关键字下标

// 并且这里还必须保证父结点关键字个数至少是t个(当然根结点除外),左右两个子结点的关键字个数是t-1

B-TREE-MERGE-CHILD(x,i)

1  y=x.ci               //获取左子结点

2  z=x.ci+1          //获取右子结点

3  y.n=2t-1        // 设置左子结点的关键字个数为最大个数

4  y.keyt=x.keyi        // 转移父结点要合并的关键字

5  for j=t+1to 2t-1   // 注意下标,转移关键字个数是t-1个

6          y.keyj=z.keyj-t     // 转移右子结点的关键字

7  if noty.leaf

8          for j=t+1 to 2t     // 孩子结点指针个数是t个

9                 y.cj=z.cj-t

10 for j=i+1 to x.n            // 父结点在i之后的、孩子结点指针都要前移

11         x.keyj-1=x.keyj    // 关键字前移一位

12         x.cj=x.cj+1            // 孩子结点指针前移一位

13 x.n=x.n-1                     //最后要将父结点的关键字个数减一

14 FREE-NODE(z)

15 DISK-WRITE(x)

16 DISK-WRITE(y)

 

 

//这里对根结点关键字只有1个,并且两个子结点关键字个数都是t-1,进行特殊处理

B-TREE-DELETE(T,k)

1 r=T.root

2  ifr.n==1

3          DISK-READ(x,c1)

4          DISK-READ(x,c2)

5          y=r.c1

6          z=r.c2

7          if y.n==z.n==t-1        // case 2c or 3b其实为什么2c也是这种情况,我也弄不明白,书上写的“可能出现”

8                 B-TREE-MERGE-CHILD(r,1)

9                 T.root=y

10                FREE-NODE(r)

11                B-TREE-DELETE-NONONE(y,k)

12         elseB-TREE-DELETE-NONONE(r,k)

13 else B-TREE-DELETE-NONONE(r,k)

 

 

// NONONE,以我的理解就是非上面的DELETE中处理的特殊情况。即根结点关键字只有1个,并且两个子结点关键字个数都是t-1

B-TREE-DELETE-NONONE(x,k)

1  i=1

2  ifx.leaf         // case1:关键字k在结点x中,并且x叶节点。疑问,如果该叶子结点关键字个数是t-1个呢(后面有回答,尽请关注)

3          while i<=x.n and k>x.keyi

4                 i=i+1

5          if k==x.keyi

6                 for j=i to x.n-1    //前移后面的关键字

7                        x.keyj=x.keyj+1

8                 x.n=x.n-1

9          DISK-WRITE(x)

10  elsewhile i<=x.n and k>x.keyi             //x是内部结点

11                i=i+1

12         DISK-READ(x.ci)      //上面的while结束后,k<=x.keyi(除了i=x.n+1)。如果k!=x.keyi,那么k必然在以x.ci为根的子树中。

13         y=x.ci           //这里i有可能是x.n+1

14         ifi<=x.n       //当然这里感觉是有个问题的,如果i=x.n+1的话,

15                DISK-READ(x.ci+1)          //就无法访问到x.ci+1

16                z=x.ci+1                              // z也就不存在,c++中,后面判断z.n>t-1那里不知道会不会出错

17         ifk==x.keyi                //case2x是内部结点,并且关键字k在结点x

18                ify.n>t-1             // case2a:结点x中前于k的子结点y至少包含t个关键字

19                       k’=B-TREE-SEARCH-PREDECESSOR(y)

20                       B-TREE-DELETE-NONONE(y,k’)             //递归地删除k’

21                       x.keyi=k’             //并在x中用k’代替k

22                elseif z.n>t-1            // case2by只有t-1个关键字,检查后于k的子结点z。就是这里,如果z不存在,这里的判断条件语句有木有问题呢?不晓得=-=

23                       k’=B-TREE-SEARCH-SUCCESSOR(z)

24                       B-TREE-DELETE-NONONE(z,k’)

25                       x.keyi=k’

26                elseB-TREE-MERGE-CHILD(x,i)      // case2c:y和z都只含有t-1个关键字,将k和z的全部合并进y

27                      B-TREE-DELETE-NONONE(y,k) // 递归地从y中删除k

28         else        // case3:关键字k不在内部结点x中,必然在以y(x.ci)为根的子树中

29                ifi>1            //获取左结点时必须左边有兄弟结点

30                       DISK-READ(x.ci-1)    //读取y结点的左兄弟结点。z就是y的右兄弟结点

31                       p=x.ci-1

32                ify.n=t-1             //如果x.ci(y)只有 t-1 个关键字,必须执行步骤3a3b来保证降至一个至少包含t个关键字的结点。这样就保证了遍历的结点关键字个数都是大于t-1,这样就解决了直接在叶子结点删除关键字的疑惑,当递归到叶子结点时,该叶子结点关键字个数必定大于t-1

33                       ifi>1 and p.n>t-1      // case3a:左兄弟结点作出牺牲。因为必须要存在左兄弟结点,所以条件i>1必不可少

34                              B-TREE-SHIFT-TO-RIGHT-CHILD(x,i-1,p,y)         //注意这里的参数,当前的下标i指向x中的关键字是在yz(如果z存在)之间的
35                       else if i<=x.nand z.n>t-1        // case3a
:右兄弟结点作出牺牲。这里也一样,因为要存在右兄弟结点,所以i<=x.n

36                              B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)         //这里的第二个参数就应该是i了,不用做手脚0.0

37                       elseif i>1            // case3b:如果说上面两种情况都不存在,也就是y结点的左右两个兄弟结点的关键字个数都只有t-1个关键字,将y(x.ci)与一个兄弟结点合并。这条支路是左兄弟结点存在的情况下,x的第 (i-1) 个关键字和y合并到左兄弟结点

38                             B-TREE-MERGE-CHILD(x,i-1)   // 注意这里下标是i-1。

39                              y=p        // 而且合并之后p就是最终合并成的结点,要把y更新一下

40                       elseB-TREE-MERGE-CHILD(x,i)      // case3b:与右兄弟结点合并。这里合并最后生成的是y结点,所以不用更新y

41               B-TREE-DELETE-NONONE(y,k) // 最后递归删除

 

 

//找出关键字k在以y为根的子树中的前驱k’

//这里的关键字ky的父结点中的关键字。

//其实仔细理解一下,就是找出以y为根的子树中的最大关键字

//刚开始我的理解是找到关键字k左边的孩子结点中的最大的关键字,但是这样是不正确的,应该把这个子树中最大的那个关键字提升到k的位置,否则B树性质不保

B-TREE-SEARCH-PREDECESSOR(y)

1  x=y

2  i=x.n

3  whilenot x.leaf

4          DISK-READ(x.ci+1)

5          x=x.ci+1

6          i=x.n

7  returnx.keyi

 

//找到后继

B-TREE-SEARCH-SUCCESSOR(z)

1  x=z

2  whilenot x.leaf

3          DISK-READ(x.c1)

4          x=x.c1

5  return x.key1

 

 

// z为主角,它原本只有t-1个关键字,需要向左兄弟结点借一个,

// yz的左兄弟结点

// x为父结点,把x中第i个关键字移到z中首个,y中最大的关键字移到x

//并且还要将z中的关键字和孩子指针(如果不是叶子结点的话)后移一位

//还要将y中最后一个孩子指针(如果不是叶结点)移到z中的第一个孩子指针

B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,y,z)

1  z.n=z.n+1

2  j=z.n

3  whilej>1

4          z.keyj=z.keyj-1

5          j=j-1

6  z.key1=x.keyi

7  x.keyi=y.keyy.n      //转移y结点最后一个关键字到x

8  if notz.leaf

9          j=z.n

10         whilej>0

11                z.cj+1=z.cj

12                j=j-1

13         z.c1=y.cy.n+1         //转移y的最后一个孩子指针到z的第一个孩子指针

14 y.n=y.n-1               //最后修改一下y的关键字个数

15 DISK-WRITE(x)

16 DISK-WRITE(y)

17 DISK-WRITE(z)

 

//从右兄弟结点借一个关键字

B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)

1 y.n=y.n+1

2  y.keyy.n=x.keyi

3  x.keyi=z.key1

4 z.n=z.n-1

5  j=1

6  whilej<=z.n

7          z.keyj=z.keyj+1

8          j=j+1

9  if notz.leaf

10         y.cy.n+1=z.c1

11         j=1

12         whilej<=z.n+1

13                z.cj=z.cj+1

14                j=j+1

15 DISK-WRITE(x)

16 DISK-WRITE(y)

17 DISK-WRITE(z)

#include <iostream>
using namespace std;

static const int t = 3;  // 设定最小度为3
static const int keyMax = 2 * t - 1;  // 关键字最大个数
static const int keyMin = t - 1;  // 关键字最小个数
static const int childMax = keyMax + 1;  // 子结点最大个数
static const int childMin = keyMin + 1;  // 子结点最小个数

// 前置声明,否则使用在Node类中使用friend会出问题
template <class T>
class BTree;

template <class T>
class Node
{
    friend class BTree<T>;  // 友元函数
public:
    // 类模板好像是不能分文件定义的,Emmm...那就直接在里面写吧
    Node()  // 构造函数,也就是生成(初始化)一个一个新结点
    {
        keyNum = 0;
        leaf = true;  // 新结点当然是叶子结点

        // 注意下面数组的下标都是从0开始,书上都是从1开始的,当心当心!!!
        for (int i = 0; i < keyMax; i++)
        {
            key[i] = '\0';  // 初始化将每个关键字设为结束符
        }

        for (int i = 0; i < childMax; i++)
        {
            pChild[i] = NULL;  // 初始化将每个子结点指针设为NULL
        }
    }
private:
    int keyNum;  // 存在的关键字个数
    bool leaf;  // 结点是否为叶子结点

    // 下面的数组直接将大小指定为最大了,
    // c++数组不支持随时更改大小
    // 后面用不到的元素设置为空便好
    T key[keyMax];  // 关键字本身
    Node<T>* pChild[childMax];  // 子结点指针数组,叶子结点没有孩子,那就全设置成NULL好了
};

// B树类
template <class T>
class BTree
{
public:
    BTree();  // B树的构造函数
    Node<T>* search(Node<T>* node, T k);  // B树的搜索

    void splitChild(Node<T>* parentNode, int index);  // 分裂B树中的结点
    bool insert(T k);  // 以沿树单程下行方式向B树插入关键字
    void insertNonfull(Node<T>* node, T k);  // 结点非满时,插入关键字

    void printPart(Node<T>* node, int num);  // 横向打印以node为根的B树
    void printAll();    // 打印整棵B树

    // 接下来是恐怖的删除部分
    bool deleteKey(T k);  // 删除关键字
    void deleteNonone(Node<T>* node, T k);  // 对于不是特殊情况进行删除操作
    void mergeChild(Node<T>* node, int i);  // 合并
    T searchPredecessor(Node<T>* node);  // 找到前驱
    T searchSuccessor(Node<T>* node);    // 找到后继
    void shiftToRightChild(Node<T>* x, int i, Node<T>* y, Node<T>* z);  // 向左兄弟结点借一个关键字
    void shiftToLeftChild(Node<T>* x, int i, Node<T>* y, Node<T>* z);  // 向右兄弟结点借一个关键字

private:
    Node<T>* root;  // B树的成员变量就只有这个根结点指针,有它就够了
};

// B树的构造函数
template <class T>
BTree<T>::BTree()
{
    root = NULL;  // 初始化
}

/*
函数名:search
函数作用:搜索B树中的关键字
函数参数:Node<T>* node(一个结点), T k(要搜索的关键字)
函数返回值:Node<T>*, 如果找到,返回该结点,否则返回NULL
*/
template <class T>
Node<T>* BTree<T>::search(Node<T>* node, T k)
{
    if (node == NULL)  // 如果这是一个空树的话,根节点是指向NULL的
    {
        return NULL;
    }
    // 根据结点的孩子数做多路分支选择,
    // 对每个内部结点,做(keyNum+1)路的分支选择
    else
    {
        int i = 0;  // 下标是从0开始
        // i的判断条件是要小于keyNum的,
        // 当i=keyNum时,就会去搜索该结点的最后一个子结点
        while(i < node->keyNum && k > node->key[i])
        {
            i++;
        }
        // 当while循环结束后,分为三种情况
        // 第一种i仍然小于keyNum,并且key[i]等于k,那便是找到了
        if (i < node->keyNum && k == node->key[i])
        {
            return node;
        }
        // 第二种:如果不满足第一种情况,也就是在该结点中没有找到关键k
        // 首先要判断该结点是否为叶子节点,如果是,那就是没有
        else if (node->leaf)
        {
            return NULL;
        }
        // 第三种:在该结点中没有找到关键字k,并且该结点不是叶子节点
        // 那便要继续向下寻找,
        // 此时的i,正好是满足 key[i-1]<k<key[i] 的,注意边界!!
        // 左边界,此时:k<key[i];右边界,此时:key[i-1]<k(此时i=keyNum)
        // 对应的,只要继续搜索pChild[i]就好了
        else
        {
            return search(node->pChild[i], k);
        }
    }
}

/*
函数名:splitChild
函数作用:分裂B树中的结点,该过程把一个满子结点分裂成两个,调整父结点,使之包含多出来的孩子
函数参数:(Node<T>* parentNode, int index)
        parentNode表示一个非满的内部结点(作为父结点),
        index表示这个父结点的满子结点的下标
函数返回值:无
*/
template <class T>
void BTree<T>::splitChild(Node<T>* parentNode, int index)
{
    Node<T>* newNode = new Node<T>();  // 创建一个新的孩子结点
    Node<T>* oldNode = parentNode->pChild[index];  // 原来的满子结点,就是要分裂它,烧死它
    
    newNode->leaf = oldNode->leaf;
    newNode->keyNum = keyMin;  // 分裂之后两个孩子都有最小的关键字个数
    
    // oldNode中原本是有keyMax(2t-1)个关键字的(满的),
    // 前面t-1个小的仍然留给旧子结点,后面t-1个大的给新子结点,最中间的关键字是要提升到父节点中去的
    // 把oldNode中keyMin(t-1)个大的关键字放到newNode中。中间关键字下标为t-1(keyMin)
    for (int i = 0; i < keyMin; i++)
    {
        newNode->key[i] = oldNode->key[keyMin + 1 + i];  // 下标开始为t,即keyMin + 1
    }

    // 如果该子结点不是叶子结点的话,还要转移一下该子结点的孩子结点指针
    // oldNode原来孩子有childMax(2t)个,分裂后孩子是对半分的
    // 前面t个孩子下标 0~t-1,后面t个孩子下标t~2t-1
    if (!oldNode->leaf)
    {
        for (int i = 0; i < childMin; i++)
        {
            newNode->pChild[i] = oldNode->pChild[childMin + i];
        }
    }

    oldNode->keyNum = keyMin;  // 调整oldNode的关键字个数为keyMin(t-1)

    // 要把父结点下标在index之后的孩子结点全部后移一位
    // 分裂之后,父结点的keyNum要多一个,孩子结点个数要多一个(变为keyNum+2)
    for (int i = parentNode->keyNum; i > index; i--)
    {
        parentNode->pChild[i + 1] = parentNode->pChild[i];  // 所以这里最后一个下标要从 keyNum+1 开始
    }

    parentNode->pChild[index + 1] = newNode;  // 那么index的下一个孩子结点就是newNode

    // 下标从index开始的关键字也通通要后移
    for (int i = parentNode->keyNum - 1; i >= index; i--)
    {
        parentNode->key[i + 1] = parentNode->key[i];
    }

    parentNode->key[index] = oldNode->key[keyMin];  // 把原来要分裂的子节点中要提升的关键字放到父结点的index位置

    parentNode->keyNum++;
}

/*
函数名:insert
函数作用:插入关键字
函数参数:T k
函数返回值:bool类型判断插入是否成功
*/
template <class T>
bool BTree<T>::insert(T k)
{
    if (search(root, k) != NULL)  // 在B树中搜索该关键字,如果返回的不是NULL,说明该关键字存在
    {
        cout << k << " 已存在" << endl;
        return false;
    }
    else
    {
        // 这里要先判断一下根节点是否指向NULL,在BTree的构造函数中,让root指向NULL了
        if (root == NULL)
        {
            root = new Node<T>();
        }

        // B树存在的情况下,如果根结点非满,可以直接使用insertNonfull
        // 但是根结点满的情况下,就要采取一下特殊的措施
        if (root->keyNum == keyMax)
        {
            Node<T>* s = new Node<T>();  // 新创建一个结点,作为新的根结点
            s->leaf = false;  // 结点初始化时是true,所以要设置一下
            s->pChild[0] = root;  // 这样原来的满根结点就成了新结点s的孩子
            splitChild(s, 0);  // 此时要分裂的就是s结点的下标为0的孩子
            root = s;  // 分裂完成之后,更新根结点
        }
        
        // 不管上面两个if走不走,最后这个活都是要干的
        insertNonfull(root, k);
        return true;  // 到这儿说明插入成功
    }
}

/*
先写非满结点的插入,再写上面的插入( ̄▽ ̄)~*
函数名:insertNonfull
函数作用:向一个非满的根结点中插入关键字
函数参数:(Node<T>* node, T k)
函数返回值:无
*/
template <class T>
void BTree<T>::insertNonfull(Node<T>* node, T k)
{
    int i = node->keyNum - 1;  // 获取结点最大关键字的下标

    // 如果该结点就是叶子结点,那就直接插入到该结点
    if (node->leaf)
    {
        while (i >= 0 && k < node->key[i])  // 循环结束时,key[i]<k<key[i+1]
        {
            node->key[i + 1] = node->key[i];
            i--;
        }
        node->key[i + 1] = k;  // 所以这里插入位置的下标是 i+1
        node->keyNum++;  // 最后让关键字个数加一
    }
    // 当该结点不是叶子结点时,继续向下一层
    else
    {
        while (i >= 0 && k < node->key[i])
        {
            i--;  // 当整个while循环结束后,key[i]<k<key[i+1]
        }
        i++;  // 所以要在这里将i加个一,此时:key[i-1]<k<key[i]
        if (node->pChild[i]->keyNum == keyMax)  // 当这个孩子结点满时,要先分裂它
        {
            splitChild(node, i);  // 分裂之后呢,node结点下标i以后的关键字都后移了,所以这里只能保证k<key[i]
            if (k > node->key[i])  // 由于上一句分裂的原因,这里需要比较
            {
                i++;  // 如果满足,下标i就要右移一位
            }
        }
        insertNonfull(node->pChild[i], k);
    }
}

/*
函数名:printPart
函数作用:横向打印
函数参数:(Node<T>* node, int num),这里的num表示的是根结点前面打印的横杆数目
函数返回值:无
*/
template <class T>
void BTree<T>::printPart(Node<T>* node, int num)
{
    if (node != NULL)  // 不要忽略了刚开始根结点指向空的情况
    {
        // 从根结点开始遍历,目的是结点中的关键字。但是,每遇到内部结点时,就要向下递归,
        for (int i = 0; i < node->keyNum; i++)
        {
            // 当结点为内部结点时,递归到下面,当然在这个for循环中会缺少最右边孩子结点的情况
            if (!node->leaf)  // 注意这里必须先判断是否为内部结点而是否进行向下递归
            {
                printPart(node->pChild[i], num + 5);  // 递归到下一层时,把num值每次增加5,这样就可以打印类似横向树状的模样
            }

            for (int j = 0; j < num; j++)  // 打印当前层的结点的关键字
            {
                cout << "-";  // 第一层,即根结点一层,打印个数由调用时决定。但每次往下一层,个数增加5
            }
            cout << node->key[i] << endl;
        }
        if (!node->leaf)  // 在for循环中无法递归到最右边的孩子结点,这里必须来这一下
        {
            printPart(node->pChild[node->keyNum], num + 5);
        }
    }
}

/*
函数名称:printAll
函数作用:打印整棵树
无参无返回值
*/
template <class T>
void BTree<T>::printAll()
{
    printPart(root, 2);
}

// 删除部分的各个函数注释就不多写了,别问为什么,伪代码已经很详细了,最主要我不想写!不想写!!!!(T_T)
/*
函数名称:deleteKey
函数作用:删除关键字k
*/
template <class T>
bool BTree<T>::deleteKey(T k)
{
    if (search(root, k) == NULL)  // 如果B树中没有该关键字,还删个屁啊
    {
        return false;
    }

    if (root->keyNum == 1)
    {
        if (root->leaf)  // 处理一下根结点是叶子结点的情况?
        {
            delete root;
            root = NULL;
            return true;
        }
        else
        {
            Node<T>* leftChild = root->pChild[0];
            Node<T>* rightChild = root->pChild[1];
            if (leftChild->keyNum == keyMin && rightChild->keyNum == keyMin)
            {
                mergeChild(root, 0);
                delete root;
                root = leftChild;
            }
        }
    }
    deleteNonone(root, k);
}

/*
函数名称:deleteNonone
函数作用:不知道(╯︵╰)
*/
template <class T>
void BTree<T>::deleteNonone(Node<T>* node, T k)
{
    int i = 0;
    if (node->leaf)  // case 1
    {
        while (i < node->keyNum && k > node->key[i])
        {
            i++;
        }
        if (node->key[i] == k)
        {
            for (int j = i; j < node->keyNum - 1; j++)
            {
                node->key[j] = node->key[j + 1];
            }
            node->keyNum--;
        }
    }
    else
    {
        Node<T>* rightChild = NULL;
        Node<T>* p = NULL;

        while (i < node->keyNum && k > node->key[i])
        {
            i++;
        }
        Node<T>* leftChild = node->pChild[i];
        if (i < node->keyNum)
        {
            //Node<T>* rightChild = node->pChild[i + 1];  // 这里rightChild作用域的原因,所以我们可以在大的作用域中先定义一下
            rightChild = node->pChild[i + 1];
        }
        
        if (node->key[i] == k)  // case 2
        {
            if (leftChild->keyNum > keyMin)  // case 2a
            {
                T k1 = searchPredecessor(leftChild);
                deleteNonone(leftChild, k1);
                node->key[i] = k1;
            }
            else if (rightChild->keyNum > keyMin)  // case 2b
            {
                T k1 = searchSuccessor(rightChild);
                deleteNonone(rightChild, k1);
                node->key[i] = k1;
            }
            else     // case 2c
            {
                mergeChild(node, i);
                deleteNonone(leftChild, k);
            }
        }
        else		// case 3
        {
            if (i > 0)
            {
                //Node<T>* p = node->pChild[i - 1];
                p = node->pChild[i - 1];
            }

            if (leftChild->keyNum == keyMin)
            {
                if (i > 0 && p->keyNum > keyMin)  // case 3a
                {
                    shiftToRightChild(node, i - 1, p, leftChild);
                }
                else if (i < node->keyNum && rightChild->keyNum > keyMin)  // case 3a
                {
                    shiftToLeftChild(node, i, leftChild, rightChild);
                }
                else if (i > 0)  // case 3b
                {
                    mergeChild(node, i - 1);
                    leftChild = p;
                }
                else			// case 3b
                {
                    mergeChild(node, i);
                }
            }
            deleteNonone(leftChild, k);
        }
    }
}

/*
函数名称:mergeChild
函数作用:合并结点
*/
template <class T>
void BTree<T>::mergeChild(Node<T>* node, int i)
{
    Node<T>* leftChild = node->pChild[i];
    Node<T>* rightChild = node->pChild[i + 1];
    leftChild->keyNum = keyMax;
    leftChild->key[keyMin]=node->key[i];
    for (int j = keyMin + 1; j < keyMax; j++)
    {
        leftChild->key[j] = rightChild->key[j - (keyMin + 1)];
    }
    if (!leftChild->leaf)
    {
        for (int j = childMin; j < childMax; j++)
        {
            leftChild->pChild[j] = rightChild->pChild[j-childMin];
        }
    }
    for (int j = i + 1; j < node->keyNum; j++)
    {
        node->key[j - 1] = node->key[j];
        node->pChild[j] = node->pChild[j + 1];
    }
    node->keyNum--;
}

/*
函数名称:searchPredecessor
函数作用:找到前驱
函数返回值:T
*/
template <class T>
T BTree<T>::searchPredecessor(Node<T>* node)
{
    Node<T>* x = node;
    int i = x->keyNum;  // 这个下标即x的最后一个孩子指针
    while (!x->leaf)
    {
        x = x->pChild[i];
        i = x->keyNum;
    }
    return x->key[i - 1];
}

/*
函数名称:searchSuccessor
函数作用:找到后继
函数返回值:T
*/
template <class T>
T BTree<T>::searchSuccessor(Node<T>* node)
{
    Node<T>* x = node;
    while (!x->leaf)
    {
        x = x->pChild[0];
    }
    return x->key[0];
}

/*
函数名称:shiftToRightChild
函数作用:向左兄弟结点借一个关键字
*/
template <class T>
void BTree<T>::shiftToRightChild(Node<T>* x, int i, Node<T>* y, Node<T>* z)
{
    z->keyNum++;
    int j = z->keyNum - 1;  // 程序中下标从0开始
    while (j > 0)
    {
        z->key[j] = z->key[j - 1];
        j--;
    }
    z->key[0] = x->key[i];
    x->key[i] = y->key[y->keyNum - 1];
    if (!z->leaf)
    {
        j = z->keyNum - 1;
        while (j >= 0)
        {
            z->pChild[j + 1] = z->pChild[j];
            j--;
        }
        z->pChild[0] = y->pChild[y->keyNum];
    }
    y->keyNum--;
}

/*
函数名称:shiftToLeftChild
函数作用:向右兄弟结点借一个关键字
*/
template <class T>
void BTree<T>::shiftToLeftChild(Node<T>* x, int i, Node<T>* y, Node<T>* z)
{
    y->keyNum++;
    y->key[y->keyNum - 1] = x->key[i];
    x->key[i] = z->key[0];
    z->keyNum--;
    int j = 0;
    while (j < z->keyNum)
    {
        z->key[j] = z->key[j + 1];
        j++;
    }
    if (!z->leaf)
    {
        y->pChild[y->keyNum] = z->pChild[0];
        j = 0;
        while (j <= z->keyNum)
        {
            z->pChild[j] = z->pChild[j + 1];
            j++;
        }
    }
}

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

int main(void)
{
    BTree<char> *bt1 = new BTree<char>();

    bt1->insert('E');
    bt1->insert('J');
    bt1->insert('D');
    bt1->insert('G');
    bt1->insert('K');
    bt1->insert('N');
    bt1->insert('O');
    bt1->insert('M');
    bt1->insert('P');
    bt1->insert('Z');
    bt1->insert('T');
    bt1->insert('X');
    bt1->insert('R');
    bt1->insert('Y');
    bt1->insert('U');
    bt1->insert('C');
    bt1->insert('V');
    bt1->insert('S');
    bt1->insert('A');

    // 插入部分
    bt1->insert('B');
    /*cout << "插入B:" << endl;
    bt1->printAll();
    cout << endl << endl;*/

    bt1->insert('Q');
    /*cout << "插入Q:" << endl;
    bt1->printAll();
    cout << endl << endl;*/

    bt1->insert('L');
    /*cout << "插入L:" << endl;
    bt1->printAll();
    cout << endl << endl;*/

    bt1->insert('F');
    /*cout << "插入F:" << endl;
    bt1->printAll();
    cout << endl << endl;*/

    
    // 删除部分
    bt1->deleteKey('F');
    cout << "删除F:" << endl;
    bt1->printAll();
    cout << endl << endl;

    bt1->deleteKey('M');
    cout << "删除M:" << endl;
    bt1->printAll();
    cout << endl << endl;

    bt1->deleteKey('G');
    cout << "删除G:" << endl;
    bt1->printAll();
    cout << endl << endl;

    bt1->deleteKey('D');
    cout << "删除D:" << endl;
    bt1->printAll();
    cout << endl << endl;

    bt1->deleteKey('B');
    cout << "删除B:" << endl;
    bt1->printAll();
    cout << endl << endl;



    system("pause");
    return 0;
}

骚话:

今天我爱你

递归到明天

终止条件(I not EXIST)

    原文作者:B树
    原文地址: https://blog.csdn.net/ZFExile/article/details/78559370
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞