【数据结构】基于树的查找法——B树(C语言)

1. m路查找树

与二叉排序树类似,可以定义一种“m叉排序树”,通常称为m路查找树
一棵m路查找树,或者是一棵空树,或者是满足如下性质的树:
① 结点最多有m棵子树,m-1个关键字,其结点结构如下图所示:
结点结构
其中,n为关键字的个数,Pi(0<=i<=n)为指向子树根结点的指针,Ki(1<=i<=n)为关键字。
② Ki<Ki+1,1<=i<=n-1
③ 子树Pi中的所有关键字均大于Ki且小于Ki+1,1<=i<=n-1
④ 子树P0中的关键字均小于K1,而子树Pn中的所有关键字均大于Kn
⑤ 子树Pi也是m路查找树,0<=i<=n
3路查找树

2. B树

一棵B树是一棵平衡的m路查找树,它或者是空树,或者是满足如下性质的树:
树中每个结点最多有m棵子树
根结点至少有两棵子树
除根结点之外的所有非叶结点至少有⌈m/2⌉棵子树
④ 所有的叶结点出现在同一层上,并且不含信息,通常称为失败结点。失败结点为虚结点,在B树中并不存在,指向它们的指针为空指针。引入失败结点是为了便于分析B树的查找性能。

B树结点结构:
B树结点结构

typedef int KeyType;
/*B树结点存储结构*/
typedef struct Mbtnode {
    
    
	struct Mbtnode* parent;		//指向双亲结点的指针
	int keynum;					//结点关键字个数
	KeyType key[m + 1];			//存放结点关键字
	struct Mbtnode* ptr[m + 1];	//指向子树的指针
}Mbtnode, * Mbtree;

问题1:具有n个非叶结点的m阶B树,至少含有多少个关键字?
分析:根据m阶B树的定义,根结点至少有两棵子树和一个关键字,除根结点之外的所有非叶结点,每个结点至少有⌈m/2⌉棵子树和⌈m/2⌉-1个关键字。综上,具有n个非叶结点的m阶B树至少含有 1+(⌈m/2⌉-1)*(n-1) 个关键字。
问题2:已知一棵3阶B树中含有2047个关键字,那么包含叶子结点(失败结点)层,该树的最大深度是多少?
分析:根据m阶B树的定义,3阶B树的根结点至少有两棵子树,除根结点之外的所有非叶结点,每个结点至少有⌈m/2⌉=2棵子树,所以该3阶B树的所有非叶结点对应一棵满二叉树,每一个结点中有一个关键字,共有2047个关键字,所以共有2047个结点,其深度为⌊log2 2047⌋+1=11,加上失败结点,最大深度为12。

3. B树查找

4阶B树

对于上图所示的4阶B树,查找过程如下:
① 查找58:由根结点指针mbt找到根结点A → 58>37,找到结点C → 40<58<85,找到结点G → 在G中找到58
② 查找32:由根结点指针mbt找到根结点A → 32<37,找到结点B → 32>25,找到结点E → 30<32<35,找到失败结点f → 查找失败

/*寻找结点中小于等于key的最大关键字序号*/
int Search(Mbtree mbt, KeyType key) {
    
    
	int i = 1;
	while (i <= mbt->keynum && mbt->key[i] <= key)
		i++;
	return (i - 1);						//返回小于等于key的最大关键字序号,为0时表示应到最左分支找,越界时表示应到最右分支找
}

/*查找关键字为k的结点*/
int srch_mbtree(Mbtree mbt, KeyType k) {
    
    
	Mbtnode* p = mbt;
	int found = 0, i;
	while (p != NULL && !found) {
    
    
		i = Search(p, k);
		if (i > 0 && p->key[i] == k)	//查找成功,found置1
			found = 1;
		else							//k不在当前结点,查找下一结点
			p = p->ptr[i];
	}
	if (found) {
    
    
		return p->key[i];
	}
	else
		return NULL;
}

4. B树插入

3阶B树插入52插入20-1插入20-2插入49-1插入49-2插入49-3

5. B树删除

5.1 在最下层结点中删除一个关键字
下图为一棵4阶B树:
4阶B树
① 当最下层结点中的关键字数目大于⌈m/2⌉-1时,可直接删除。
如删除11和53时,可直接删除,结果如下:
删除1
② 当最下层待删关键字所在结点中的关键字数目为最低要求⌈m/2⌉-1时,如果其左(右)兄弟中关键字数目大于⌈m/2⌉-1,则可采用“父子换位法”。
如删除39时,为保持其“中序有序”,可将父结点中43下移至39处,而将右兄弟中最左边的47上移至原43处,结果如下:
删除2
③ 当最下层待删关键字所在结点及其左(右)兄弟中的关键字数目均为最低要求⌈m/2⌉-1时,需要进行合并处理,合并过程与插入时的分裂过程“互逆”,合并一次,分支数少一,可能出现连锁合并,当合并到根时,各分支深度同时减1。
如删除64时,为保持各分支等长,将删除64后的剩余信息及78合并入右兄弟,结果如下:
删除3④ 删除27时,首先将剩余信息与父结点中的18并入左兄弟,并释放空结点。
此时父结点也需要合并,将父结点中的剩余信息(指针p1)与祖父结点中的35并入47左端。
至此,祖父结点仍需合并,但由于待合并结点的父指针为NULL,故停止合并,直接将根指针BT置为指针p2的值,释放空结点。
删除4
5.2 在非最下层结点中删除一个关键字
在下图所示4阶B树删除43、35过程:
① 删除43:在保持“中序有序”的前提下,可将43“右子树”中的最小值47顶替43,然后将原47删除。
② 删除35:用35“左子树”的“右下端”元素27代替35,然后再删除原27。
删除5
参考:耿国华《数据结构——用C语言描述(第二版)》

更多数据结构内容关注我的《数据结构》专栏https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482

猜你喜欢

转载自blog.csdn.net/weixin_51450101/article/details/123539334