算法导论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
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注