B树的代码实现

B树的原理在这篇博客https://blog.csdn.net/xiaoan08133192/article/details/115622370中进行了介绍,这里是B树的代码实现。B树的插入和删除函数,在遍历的时候都是使用不回溯的方法,插入操作在对a结点插入数据的时,已经保证了a的关键字个数小于2*M - 1,删除操作在对a结点删除数据的时,已经保证了a的关键字个数大于M - 1。如何保证这一点是代码的精髓

//M为B树的最小度
#define M 2

typedef struct btree_node{
	int key[2 * M - 1];
	struct btree_node* ptr[2 * M];
	int num;
	bool is_leaf;
}Btree;

//查找key关键字是否在B树中,如果在返回其所在结点
//时间复杂度为O(t*logn)
btree_node* search(btree_node* node, int key){
	int num = node -> num;
	for(int i = 0; i < num; i++){
		if(key < node -> key[i]){
			break;
		}
	}

	if(key == node -> key[i]){
		return node;
	}

	if(node -> is_leaf){
		return NULL;
	}

	return search(node -> ptr[i], key);
}

//创建一棵B树,时间复杂度是O(1)
btree_node* create(){
	btree_node* root = new btree_node();
	root -> is_leaf = true;
	root -> num = 0;
	return root;
}

void bTreeSplitChild(btree_node* node, int i){
	btree_node* rightChild = new btree_node();
	btree_node* leftChild = node -> ptr[i];
	rightChild -> is_leaf = leftChild -> is_leaf;
	rightChild -> num = M - 1;
	int j;
	//移动node[i]结点key
	for(j = 1; j <= M - 1; j++){
		rightChild -> key[j - 1] = leftChild -> key[j - 1 + M];
	}

	//移动node[i]非叶子结点的指针
	if(!leftChild -> is_leaf){
		for(int j = 1; j <= M; j++){
			rightChild -> key[j - 1] = leftChild -> key[j - 1 + M];
		}
	}
	//设置node[i]结点的num
	leftChild -> num = M - 1;

	//移动node结点的key
	for(j = node -> num - 1; j >= i; j--){
		node -> key[j + 1] = node -> key[j];
	}

	node -> key[i] = leftChild -> key[M];
	//移动node结点的指针
	for(j = node -> num; j >= i; j--){
		node -> ptr[j + 1] = node -> ptr[j];
	}

	node -> key[i] = leftChild -> key[M];
	node -> ptr[i + 1] = rightChild;
	//设置node结点的num和指针
	node -> num++;
}

//在没有满的node结点插入key
void bTreeInsertNonfull(btree_node* node, int key){
	int num = node ->num;
	if(node ->is_leaf){
		while(num >= 1 && key < node ->key[num - 1]){
			node ->key[num] = node ->key[num - 1];
			num--;
		}
		node ->key[num] = key;
		node ->num++;
	}else{//非叶子结点的情况下
		while(num >= 1 && key < node ->key[num - 1]){
			num--;
		}
		if(node ->ptr[num] ->num == 2 * M - 1){//下面要遍历的这个节点需要分裂
			bTreeSplitChild(node, num);
			//分裂之后,node结点多一个key,这一步的目的是看需要插入到新增key的左边还是右边
			if(key > node ->key[num]){
				num++;
			}
		}
		bTreeInsertNonfull(node ->ptr[num], key);
	}
}

btree_node* bTreeInsert(btree_node* root, int key){
	//和二叉搜索树不同,b树高度的增加发生在顶部,而非底部
	if(root -> num == 2 * M - 1){
		btree_node* node = new btree_node();
		node ->is_leaf = false;
		node ->num = 0;
		node ->ptr[0] = root;
		bTreeSplitChild(node, 0);
		root = node;//新的树根结点		
	}
	bTreeInsertNonfull(root, key);
	return root;
}

//node ->ptr[pos]和node ->ptr[pos + 1]合并
void btree_merge_child(btree_node* node, int pos){
	btree_node* left = node ->ptr[pos];
	btree_node* right = node ->ptr[pos + 1];
	left ->key[M - 1] = node ->key[pos];
	int i;

	for(i = M; i < 2 * M - 1; i++){
		left ->key[i] = right ->key[i - M];
	}

	//如果z非叶子,需要拷贝指针
	if(false == right ->is_leaf){
		for(i = M; i < 2 * M; i++){
			left ->ptr[i] = right ->ptr[i - M];
		}
	}
	left -> num = 2*M-1;

	//处理node,这时node不可能是叶子结点
	for(i = pos + 1; i < node ->num; i++){
		node ->key[i - 1] = node ->key[i];
		node ->ptr[i - 1] = node ->ptr[i];
		if(i == node ->num - 1){
			//处理最后一个指针
			node ->ptr[node ->num - 1] = node ->ptr[node ->num];
		}
	}
	node ->num--;
	delete right;
}




//向root->ptr[pos - 1]结点借一个关键字
void btreeShiftLeftToRight(btree_node* root, int pos){
	int i;
	//移动root ->ptr[pos]结点中的key
	for(i = root ->ptr[pos] ->num - 1; i > 0; i--){
		root ->ptr[pos] ->key[i] = root ->ptr[pos] ->key[i - 1];
	}

	root ->ptr[pos] ->key[0] = root ->key[pos];
	root ->key[pos] = root ->ptr[pos - 1] ->key[root ->ptr[pos - 1] ->num - 1];

	//移动root ->ptr[pos]结点中的ptr
	if(false == root ->ptr[pos] ->is_leaf){
		for(i = root ->ptr[pos] ->num; i > 0; i--){
			root ->ptr[pos] ->ptr[i] = root ->ptr[pos] ->ptr[i - 1];
		}
		root ->ptr[pos] ->ptr[0] = root ->ptr[pos - 1] ->ptr[root ->ptr[pos - 1] ->num];
	}
	root ->ptr[pos - 1] ->num -= 1;
	root ->ptr[pos] ->num += 1;
}

//向root->ptr[pos + 1]结点借一个关键字
void btreeShiftRightToLeft(btree_node* root, int pos){
	int i;	
	root ->ptr[pos] ->key[root ->ptr[pos] ->num] = root ->key[pos];
	root ->key[pos] = root ->ptr[pos + 1] ->key[0];
	root ->ptr[pos] ->ptr[root ->ptr[pos] ->num + 1] = root ->ptr[pos + 1] ->ptr[0];
	root ->ptr[pos] ->num += 1;

	//移动root->ptr[pos + 1]中的key和ptr
	for(i = 1; i < root ->ptr[pos + 1] ->num - 1; i++){
		root ->ptr[pos + 1] ->key[i - 1] = root ->ptr[pos + 1] ->key[i];
	}

	if(false == root ->ptr[pos + 1] ->is_leaf){
		for(i = 1; i < root ->ptr[pos + 1] ->num; i++){
			root ->ptr[pos + 1] ->ptr[i - 1] = root ->ptr[pos + 1] ->ptr[i];
		}
	}


	root ->ptr[pos + 1] ->num -= 1;
}

//寻找以root为根的最大关键字
int btree_search_predecessor(btree_node *root)
{
    while(false == root->is_leaf) {
        root = root->ptr[root->num];
    }
    return root->key[root->num-1];
}

//寻找以root为根的最小关键字
int btree_search_successor(btree_node *root)
{
    while(false == root->is_leaf) {
        root = root->ptr[0];
    }
    return root->key[0];
}

void btree_delete_nonone(btree_node* root, int key){
	int i,j;
	if(true == root ->is_leaf){//如果在叶子结点
		i = 0;
		while(i < root ->num && key > root ->key[i]) i++;
		if(key == root ->key[i]){
			for(j = i + 1; j < 2*M - 1; j++){
				root ->key[j - 1] = root ->key[j];
			}
			//叶子结点不需要处理指针
			root ->num--;
		}else{
			cout << "no " << key << endl;
		}
	}else{//root不是叶子结点,这里分为两种情况
		//一种是key在root结点,另一种是key不再root结点
		i = 0;
		while(i < root ->num && key > root ->key[i]) i++;
		if(i < root ->num && key == root ->key[i]){
			if(root ->ptr[i] ->num > M - 1){
			//找key的前驱结点代替key,递归删除key的前驱结点
				int pre = btree_search_predecessor(root ->ptr[i]);
				root ->key[i] = pre;
				btree_delete_nonone(root ->ptr[i], pre);
			}else if(root ->ptr[i + 1] ->num > M - 1){
			//找key的后继结点代替key,递归删除key的后继结点
				int next = btree_search_successor(root ->ptr[i + 1]);
				root ->key[i + 1] = next;				
				btree_delete_nonone(root ->ptr[i + 1], next);
			}else{
			// 两个分支节点数都为M-1,将两个节点合并,并在root ->ptr[i]中递归删除target,
				btree_merge_child(root, i);
				btree_delete(root ->ptr[i], key);
			}
		}else{
			btree_node* leftbrother = NULL;
			btree_node* rightbrother = NULL;
			if(i < root ->num) rightbrother = root ->ptr[i+1];
			if(i > 0) leftbrother = root ->ptr[i-1];

			if(root ->ptr[i] ->num == M - 1){
				if(i > 0 && leftbrother ->num > M - 1){
					// 左邻接节点关键字个数大于M-1
					btreeShiftLeftToRight(root, i);
				}else if(i < root ->num && rightbrother ->num > M - 1){
					// 右邻接节点关键字个数大于M-1
					btreeShiftRightToLeft(root, i);
				}else{
					//选择一个相邻的节点合并
					if(i > 0){//有左兄弟
						btree_merge_child(root, i - 1);
						root = root ->ptr[i - 1]; //合并之后,在哪里继续遍历
					}else{
						btree_merge_child(root, i);
						root = root ->ptr[i];
					}
					btree_delete_nonone(root, key);
				}
			}else{
				btree_delete_nonone(root ->ptr[i], key);
			}
		}
	}
}

btree_node* btree_delete(btree_node* node, int key){
	//当root只有一个关键字且两个子女的关键字个数都为M-1时,合并根与两个子女
    //这是唯一能降低树高的情形
	if(NULL != node && 1 == node -> num){
		if(NULL != node ->ptr[0] && NULL != node ->ptr[1] && node ->ptr[0] ->num == M - 1 && node -> ptr[1] ->num == M - 1){
			btree_merge_child(node, 0);
			node = node ->ptr[0];
		}
	}
	btree_delete_nonone(node, key);
	return node;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
B树是一种常用的平衡搜索树,用于在大型数据集上进行高效的插入、删除和查找操作。它具有以下特点:每个节点可以容纳多个键值对,节点的子节点数目可以不一致。在B树中,每个节点的子节点数目通常被限制在一定范围内,这个范围由一个参数t控制。对于每个内部节点,它包含了t-1个键和t个子节点。对于每个叶子节点,它包含了t-1个键,但没有子节点。B树的高度通常比较低,因此它的查找操作非常高效。 关于B树代码实现,可以使用Python编程语言来实现。你可以使用一个包含B树节点的类来表示B树,每个节点包含键和对应的值,以及子节点的引用。在插入数据时,可以按照B树的特性进行递归操作,将数据插入到合适的位置,并保持树的平衡。代码实现过程中,***```python class BTreeNode: def __init__(self, leaf=False): self.keys = [] self.values = [] self.children = [] self.leaf = leaf class BTree: def __init__(self, t): self.root = BTreeNode(leaf=True) self.t = t def insert(self, key, value): if len(self.root.keys) == (2 * self.t) - 1: new_root = BTreeNode() new_root.children.append(self.root) self.root = new_root self.split_child(self.root, 0) self.insert_non_full(self.root, key, value) def insert_non_full(self, node, key, value): i = len(node.keys) - 1 if node.leaf: node.keys.append(None) node.values.append(None) while i >= 0 and key < node.keys[i]: node.keys[i + 1 = node.keys[i] node.values[i + 1 = node.values[i] i -= 1 node.keys[i + 1 = key node.values[i + 1 = value else: while i >= 0 and key < node.keys[i]: i -= 1 i += 1 if len(node.children[i].keys) == (2 * self.t) - 1: self.split_child(node, i) if key > node.keys[i]: i += 1 self.insert_non_full(node.children[i], key, value) def split_child(self, parent, index): t = self.t child = parent.children[index] new_node = BTreeNode(leaf=child.leaf) parent.keys.insert(index, child.keys[t - 1]) parent.values.insert(index, child.values[t - 1]) parent.children.insert(index + 1, new_node) new_node.keys = child.keys[t:] new_node.values = child.values[t:] child.keys = child.keys[:t - 1] child.values = child.values[:t - 1] if not child.leaf: new_node.children = child.children[t:] child.children = child.children[:t] # 创建一个B树对象 btree = BTree(3) # 插入数据 btree.insert(1, 'value1') btree.insert(2, 'value2') btree.insert(3, 'value3') # 输出B树结构 print(btree.root.keys) # 输出根节点的键 print(btree.root.values) # 输出根节点的值 print(btree.root.children) # 输出根节点的子节点 ``` 这是一个简单的B树代码实现,其中根据t的值对节点进行分裂和合并操作,以保证B树的平衡性。在插入数据时,将键和值插入到合适的位置,并根据需要分裂节点。这个示例只是B树的基本实现,你可以根据具体的需求进行扩展和优化。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [BPlusTree:B+树的python实现](https://download.csdn.net/download/weixin_42152298/19487400)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [航信eTerm放大系统JAVA版本](https://download.csdn.net/download/shenjianxin/88255239)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值