数据结构 b树(b-)树

头文件

cpp 复制代码
#pragma on
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
using namespace std;
#define M 5//阶数
typedef struct btnode {
    int keynum;                 // 当前关键字数量
    int keys[M];                // 关键字数组(下标0开始使用,keys[keynum]作为哨兵)
    struct btnode* parent;      // 父节点指针
    struct btnode* ptr[M + 1];  // 子树指针数组(ptr[keynum]作为哨兵)
} btnode;

// B-树结构
typedef struct btree {
    btnode* root;               // 根节点
    int size;                   // 树中关键字总数(可选)
} btree;

// 查找结果结构
typedef struct Result {
    btnode* pNode;              // 找到的节点
    int index;                  // 关键字位置(或应该插入的位置)
    bool found;                 // 是否找到
} Result;
//工具函数
//1.申请Result结构体变量,用于节点查找结构的返回
Result* Buy_ResultNode();

//2.购买新节点,用于分裂
btnode* Buy_Node();

//3.插入的调整函数(可能不止一次的上溢出,所以分裂也可能不止一次)
void Insert_Adjust(btree* pTree, btnode* node);

//4.插入的单次分裂(上溢出了,需要一次分裂)
btnode* SplitNode(btree* pTree, btnode* node);

//普通函数
//1.初始化
void Init_btree(btree* pTree);

//2.查找
Result* Search_btree(btree* pTree, int val);

//3.插入
bool Insert_btree(btree* pTree, int val);

//4.打印(中序遍历)
void Show_InOrder(btnode* root);//递归

//5.删除
bool Delete_btree(btree* pTree, int val);

//6.删除的调整函数
void Delete_Adjust(btree* pTree, btnode* Node);

//问兄弟借元素(通用的)//pp是,当前节点和借的兄弟节点的父节点  index:当前节点和借的兄弟节点夹住的父节点的元素下标

//7.从左兄弟借
void BorrowFrom_LeftBro(btnode* pp, int index);
//8.从右兄弟借
void BorrowFrom_RightBro(btnode* pp, int index);

//问兄弟借操作,父节点不会少元素,所以不需要进行连续的下溢出判断
//但是和兄弟合并操作,父节点会少一个元素,所以需要进行连续的下溢出判断

//9.和左兄弟合并 (把合并之后的节点返回出来)
btnode* Merge_LeftBro(btnode* pp, int index);

//10.和右兄弟合并
btnode* Merge_RightBro(btnode* pp, int index);

源文件

cpp 复制代码
#include"b-树.h"
//工具函数
//1.申请Result结构体变量,用于节点查找结构的返回
Result* Buy_ResultNode() {
	Result* res = (Result*)malloc(sizeof(Result));
	if (!res) {
		perror("Failed to allocate result");
		exit(EXIT_FAILURE);
	}
	res->pNode = nullptr;
	res->index = -1;
	res->found = false;
	return res;
}

//2.购买新节点,用于分裂
btnode* Buy_Node() {
	btnode* newnode = (btnode*)malloc(sizeof(btnode));
	if (newnode == nullptr) {
		cout << "buynode err" << endl;
		return nullptr;
	}
	newnode->keynum = 0;
	newnode->parent = nullptr;
	for (int i = 0; i <= M; i++) {
		newnode->ptr[i] = NULL;
	}
	return newnode;
}



//普通函数
//1.初始化
void Init_btree(btree* pTree) {
	assert(pTree != nullptr);
	pTree->root = nullptr;
	pTree->size = 0;
}
// 在节点中查找关键字位置(二分查找优化)
int FindPos(btnode* node, int val) {
	int left = 0, right = node->keynum - 1;
	while (left <= right) {
		int mid = left + (right - left) / 2;
		if (node->keys[mid] == val) {
			return mid;
		}
		else if (node->keys[mid] < val) {
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}
	return left; // 返回应该插入的位置
}
//2.查找
Result* Search_btree(btree* pTree, int val) {
	assert(pTree != nullptr);
	Result* res = Buy_ResultNode();
	btnode* cur = pTree->root;
	while (cur) {
		int pos = FindPos(cur, val);
		if (pos < cur->keynum && cur->keys[pos] == val) {
			res->pNode = cur;
			res->index = pos;
			res->found = true;
			return res;
		}
		res->pNode = cur;
		res->index = pos;
		// 继续向下查找
		cur = cur->ptr[pos];
	}
	// 没找到
	res->found = false;
	return res;
}

//3.插入
bool Insert_btree(btree* pTree, int val) {
	assert(pTree != nullptr);
	if (pTree->root == NULL) {
		btnode* new_node = Buy_Node();
		new_node->keys[0] = val;
		new_node->keynum = 1;
		pTree->root = new_node;
		pTree->size++;
		return true;
	}
	// 查找插入位置
	Result* res = Search_btree(pTree, val);
	if (res->found) {
		free(res);
		return false;  // 关键字已存在
	}
	// 在叶子节点插入
	btnode* leaf = res->pNode;
	int pos = res->index;
	free(res);
	// 移动关键字,腾出位置
	for (int i = leaf->keynum; i > pos; i--) {
		leaf->keys[i] = leaf->keys[i - 1];
	}
	leaf->keys[pos] = val;
	leaf->keynum++;
	pTree->size++;
	if (leaf->keynum == M) {  // 节点已满(上溢)
		Insert_Adjust(pTree, leaf);
	}
	return true;
}
//插入的调整函数(可能不止一次的上溢出,所以分裂也可能不止一次)
void Insert_Adjust(btree* pTree, btnode* node) {
	assert(pTree != nullptr && node != nullptr);
	while (node != nullptr && node->keynum == M) {
		btnode* right = SplitNode(pTree, node);
		int mid_key = node->keys[M / 2];//中间关键字(要提升)
		// 更新原节点关键字数(去掉中间关键字)
		node->keynum = M / 2;
		// 如果node是根节点
		if (node->parent == NULL) {
			btnode* new_root = Buy_Node();
			new_root->keys[0] = mid_key;
			new_root->keynum = 1;
			new_root->ptr[0] = node;
			new_root->ptr[1] = right;
			node->parent = new_root;
			right->parent = new_root;
			pTree->root = new_root;
			break;
		}
		// node有父节点
		else {
			btnode* parent = node->parent;
			int pos = FindPos(parent, mid_key);
			// 在父节点中插入中间关键字
			for (int i = parent->keynum; i > pos; i--) {
				parent->keys[i] = parent->keys[i - 1];
				parent->ptr[i + 1] = parent->ptr[i];
			}
			parent->keys[pos] = mid_key;
			parent->ptr[pos] = node;        // 左子树
			parent->ptr[pos + 1] = right;   // 右子树
			parent->keynum++;
			// 设置父指针
			right->parent = parent;
			// 继续向上检查
			node = parent;
		}
	}
}

//插入的单次分裂(上溢出了,需要一次分裂)
btnode* SplitNode(btree* pTree, btnode* node) {
	int mid = M / 2;
	// 创建右节点
	btnode* right = Buy_Node();
	right->parent = node->parent;
	// 将右半部分关键字移到新节点(跳过中间关键字)
	int j = 0;
	for (int i = mid + 1; i < M; i++) {
		right->keys[j] = node->keys[i];
		j++;
	}
	right->keynum = j;
	// 如果不是叶子节点,复制子树指针
	if (node->ptr[0] != nullptr) {
		j = 0;
		for (int i = mid + 1; i <= M; i++) {
			right->ptr[j] = node->ptr[i];
			if (right->ptr[j]) {
				right->ptr[j]->parent = right;
			}
			j++;
		}
	}
	return right;
}
//4.打印(中序遍历)
void Show_InOrder(btnode* root){//递归
	if (root == nullptr) return;
	for (int i = 0; i < root->keynum; i++) {
		// 先遍历左子树
		if (root->ptr[i] != NULL) {
			Show_InOrder(root->ptr[i]);
		}
		// 访问当前关键字
		printf("%d ", root->keys[i]);
	}
	// 最后遍历最右边的子树
	if (root->ptr[root->keynum] != NULL) {
		Show_InOrder(root->ptr[root->keynum]);
	}
}

//5.删除
bool Delete_btree(btree* pTree, int val) {
	assert(pTree != nullptr);
	Result* res = Search_btree(pTree, val);
	if (!res->found) {
		free(res);
		return false;
	}
	btnode* node = res->pNode;
	int index = res->index;
	free(res);
	// 情况1:在叶子节点
	if (node->ptr[0] == NULL) {
		// 直接删除关键字
		for (int i = index; i < node->keynum - 1; i++) {
			node->keys[i] = node->keys[i + 1];
		}
		node->keynum--;
		pTree->size--;
		// 检查下溢
		if (node->keynum < (M + 1) / 2 - 1 && node != pTree->root) {
			Delete_Adjust(pTree, node);
		}
		// 如果根节点变空
		if (pTree->root->keynum == 0) {
			btnode* old_root = pTree->root;
			if (pTree->root->ptr[0]) {
				pTree->root = pTree->root->ptr[0];
				pTree->root->parent = NULL;
			}
			else {
				pTree->root = NULL;
			}
			free(old_root);
		}
		return true;
	}
	// 情况2:在内部节点
	else {
		// 找到前驱(左子树的最大值)
		btnode* pred_node = node->ptr[index];
		while (pred_node->ptr[pred_node->keynum] != NULL) {
			pred_node = pred_node->ptr[pred_node->keynum];
		}
		int pred_val = pred_node->keys[pred_node->keynum - 1];
		// 用前驱替换要删除的关键字
		node->keys[index] = pred_val;
		// 删除前驱(递归调用)
		return Delete_btree(pTree, pred_val);
	}
}

//6.删除的调整函数
void Delete_Adjust(btree* pTree, btnode* node) {
	assert(pTree != nullptr && node != nullptr);
	if (node == pTree->root || node->keynum >= (M + 1) / 2 - 1) {
		return;  // 根节点或关键字足够
	}
	btnode* parent = node->parent;
	if (!parent) return;
	// 找到node在父节点中的位置
	int index = 0;
	while (index <= parent->keynum && parent->ptr[index] != node) {
		index++;
	}
	// 尝试从左兄弟借
	if (index > 0) {
		btnode* left_bro = parent->ptr[index - 1];
		if (left_bro->keynum > (M + 1) / 2 - 1) {
			BorrowFrom_LeftBro(parent, index);
			return;
		}
	}
	// 尝试从右兄弟借
	if (index < parent->keynum) {
		btnode* right_bro = parent->ptr[index + 1];
		if (right_bro->keynum > (M + 1) / 2 - 1) {
			BorrowFrom_RightBro(parent, index);
			return;
		}
	}
	// 合并操作
	btnode* merged;
	if (index > 0) {
		// 与左兄弟合并
		merged = Merge_LeftBro(parent, index);
	}
	else {
		// 与右兄弟合并
		merged = Merge_RightBro(parent, index);
	}
	// 检查父节点是否需要调整
	Delete_Adjust(pTree, parent);
}

//问兄弟借元素(通用的)
//pp是,当前节点和借的兄弟节点的父节点  
//index:当前节点和借的兄弟节点夹住的父节点的元素下标
//7.从左兄弟借
void BorrowFrom_LeftBro(btnode* pp, int index) {
	assert(pp != nullptr);
	btnode* left_bro = pp->ptr[index - 1];
	btnode* cur = pp->ptr[index];
	// cur右移关键字腾出位置
	for (int i = cur->keynum; i > 0; i--) {
		cur->keys[i] = cur->keys[i - 1];
	}
	for (int i = cur->keynum + 1; i > 0; i--) {
		cur->ptr[i] = cur->ptr[i - 1];
	}
	// 父节点关键字下移
	cur->keys[0] = pp->keys[index - 1];
	cur->keynum++;
	// 左兄弟最后一个关键字上移
	pp->keys[index - 1] = left_bro->keys[left_bro->keynum - 1];
	// 左兄弟最后一个子树移到cur
	if (!left_bro->ptr[0]) {  // 如果不是叶子节点
		cur->ptr[0] = left_bro->ptr[left_bro->keynum];
		if (cur->ptr[0]) {
			cur->ptr[0]->parent = cur;
		}
	}
	left_bro->keynum--;
}
//8.从右兄弟借
void BorrowFrom_RightBro(btnode* pp, int index) {
	assert(pp != nullptr);
	btnode* cur = pp->ptr[index];
	btnode* right_bro = pp->ptr[index + 1];
	// 父节点关键字下移
	cur->keys[cur->keynum] = pp->keys[index];
	cur->keynum++;
	// 右兄弟第一个关键字上移
	pp->keys[index] = right_bro->keys[0];
	// 右兄弟关键字左移
	for (int i = 0; i < right_bro->keynum - 1; i++) {
		right_bro->keys[i] = right_bro->keys[i + 1];
	}
	// 右兄弟子树左移
	if (!right_bro->ptr[0]) {  // 如果不是叶子节点
		cur->ptr[cur->keynum] = right_bro->ptr[0];
		if (cur->ptr[cur->keynum]) {
			cur->ptr[cur->keynum]->parent = cur;
		}
		for (int i = 0; i < right_bro->keynum; i++) {
			right_bro->ptr[i] = right_bro->ptr[i + 1];
		}
	}
	right_bro->keynum--;
}

//问兄弟借操作,父节点不会少元素,所以不需要进行连续的下溢出判断
//但是和兄弟合并操作,父节点会少一个元素,所以需要进行连续的下溢出判断
//9.和左兄弟合并 (把合并之后的节点返回出来)
btnode* Merge_LeftBro(btnode* pp, int index) {
	btnode* left_bro = pp->ptr[index - 1];
	btnode* cur = pp->ptr[index];
	// 父节点关键字下移
	left_bro->keys[left_bro->keynum] = pp->keys[index - 1];
	left_bro->keynum++;
	// 复制cur的关键字到左兄弟
	for (int i = 0; i < cur->keynum; i++) {
		left_bro->keys[left_bro->keynum + i] = cur->keys[i];
	}
	// 复制cur的子树到左兄弟(如果不是叶子节点)
	if (!cur->ptr[0]) {
		for (int i = 0; i <= cur->keynum; i++) {
			left_bro->ptr[left_bro->keynum + i] = cur->ptr[i];
			if (left_bro->ptr[left_bro->keynum + i]) {
				left_bro->ptr[left_bro->keynum + i]->parent = left_bro;
			}
		}
	}
	left_bro->keynum += cur->keynum;
	// 父节点关键字左移
	for (int i = index - 1; i < pp->keynum - 1; i++) {
		pp->keys[i] = pp->keys[i + 1];
	}
	// 父节点子树左移
	for (int i = index; i < pp->keynum; i++) {
		pp->ptr[i] = pp->ptr[i + 1];
	}
	pp->keynum--;
	// 释放cur节点
	free(cur);
	return left_bro;
}

//10.和右兄弟合并
btnode* Merge_RightBro(btnode* pp, int index) {
	assert(pp != nullptr);
	btnode* cur = pp->ptr[index];
	btnode* right_bro = pp->ptr[index + 1];
	// 父节点关键字下移
	cur->keys[cur->keynum] = pp->keys[index];
	cur->keynum++;
	// 复制右兄弟的关键字
	for (int i = 0; i < right_bro->keynum; i++) {
		cur->keys[cur->keynum + i] = right_bro->keys[i];
	}
	// 复制右兄弟的子树(如果不是叶子节点)
	if (!right_bro->ptr[0]) {
		for (int i = 0; i <= right_bro->keynum; i++) {
			cur->ptr[cur->keynum + i] = right_bro->ptr[i];
			if (cur->ptr[cur->keynum + i]) {
				cur->ptr[cur->keynum + i]->parent = cur;
			}
		}
	}
	cur->keynum += right_bro->keynum;
	// 父节点关键字左移
	for (int i = index; i < pp->keynum - 1; i++) {
		pp->keys[i] = pp->keys[i + 1];
	}
	// 父节点子树左移
	for (int i = index + 1; i < pp->keynum; i++) {
		pp->ptr[i] = pp->ptr[i + 1];
	}
	pp->keynum--;
	// 释放右兄弟节点
	free(right_bro);
	return cur;
}
相关推荐
陌路202 小时前
S31 B树详解
数据结构·b树
Boop_wu2 小时前
[Java 数据结构] 图(1)
数据结构·算法
无尽的罚坐人生2 小时前
hot 100 128. 最长连续序列
数据结构·算法·贪心算法
Savior`L2 小时前
基础算法:模拟、枚举
数据结构·c++·算法
white-persist3 小时前
【内网运维】Netsh 全体系 + Windows 系统专属命令行指令大全
运维·数据结构·windows·python·算法·安全·正则表达式
TechNomad3 小时前
哈希表的原理详解
数据结构·哈希算法
蒙奇D索大3 小时前
【数据结构】排序算法精讲 | 快速排序全解:高效实现、性能评估、实战剖析
数据结构·笔记·学习·考研·算法·排序算法·改行学it
@小码农3 小时前
2025年12月 GESP认证 图形化编程 一级真题试卷(附答案)
开发语言·数据结构·算法
小袁顶风作案4 小时前
leetcode力扣——27.移除元素、26.删除有序数组的重复项、80.删除有序数组中的重复项 II
数据结构·算法·leetcode