码迷,mamicode.com
首页 > 其他好文 > 详细

b树的实现

时间:2015-05-04 10:12:01      阅读:109      评论:0      收藏:0      [点我收藏+]

标签:b树   数据结构   

#include <stdio.h>
#include <iostream>
#include <queue>
#include <cstdlib>
#include <ctime>

#define M 2

using namespace std;

struct BTNode{
	int keyNum;
	int key[2*M-1];  //关键字数组
	struct BTNode* child[2*M];//孩子结点数组
	bool isLeaf;
};

void DiskRead(struct BTNode *pNode)
{
	//for (int i = 0; i < pNode->keyNum; i++)
		//cout << (char)(pNode->key[i]) << " ";
	//cout << endl;
}

void DiskWrite(struct BTNode* pNode)
{
	//for (int i = 0; i < pNode->keyNum; i++)
		//cout << char(pNode->key[i]) << " ";
	//cout << endl;
}

struct BTNode* search(struct BTNode* pNode, int key,int &index)
{
	int i = 0;
	while (i<pNode->keyNum&&key>pNode->key[i])//找到第一个大于等于key的下标
		i++;
	if (i < pNode->keyNum&&key == pNode->key[i]){//如果找到关键字,返回
		index = i;
		return pNode;
	}
	if (pNode->isLeaf)//已经搜到叶子结点,不存在
		return NULL;
	else{
		DiskRead(pNode->child[i]);
		return search(pNode->child[i], key, index);//在第一个大于key值的孩子节点中递归搜索
	}
}


struct BTNode* create(void)
{
	struct BTNode* root = new BTNode();
	root->isLeaf = true;
	root->keyNum = 0;
	DiskWrite(root);
	return root;
}

//当child结点有2M-1个关键字时,分裂此结点
void SplitChild(struct BTNode* parent, int i, struct BTNode* child)
{
	int j;
	struct BTNode* pNode = new BTNode();
	pNode->isLeaf = child->isLeaf;
	pNode->keyNum = M - 1;
	for (j = 0; j < M - 1; j++)//将child结点的后M-1个关键字赋给新节点
		pNode->key[j] = child->key[j + M];
	if (!child->isLeaf){//如果child不是叶子结点,将其后M个孩子结点赋给新节点。
		for (j = 0; j < M; j++)
			pNode->child[j] = child->child[j + M];
	}
	child->keyNum = M - 1;
	
	for (j = parent->keyNum; j > i; j--)
		parent->child[j + 1] = parent->child[j];//将child结点的父节点parent下标i以后的结点指针都向后移动一位,
	parent->child[j + 1] = pNode;//将新生成的结点当成parent的一个孩子
	for (j = parent->keyNum - 1; j >= i; j--)	//将i后面的关键字都向后移动一位
		parent->key[j + 1] = parent->key[j];
	parent->key[j + 1] = child->key[M-1];//将孩子结点的中间结点移到父节点的指定位置
	parent->keyNum++;
	DiskWrite(parent);
	DiskWrite(pNode);
	DiskWrite(child);
}

//在插入时,保证pNode结点的关键字少于2*M-1个
void InsertNonFull(struct BTNode* pNode, int key)
{
	int i = pNode->keyNum - 1;
	if (pNode->isLeaf){//如果是叶子结点,直接插入
		while (i >= 0 && key < pNode->key[i]){
			pNode->key[i + 1] = pNode->key[i];
			i--;
		}
		pNode->key[i + 1] = key;
		pNode->keyNum++;
		DiskWrite(pNode);
	}
	else {
		while (i >= 0 && key < pNode->key[i])
			i--;//找到第一个小于等于key的下标
		i++;
		DiskRead(pNode->child[i]);
		if (pNode->child[i]->keyNum == 2 * M - 1){//判断孩子结点是否有2*M-1个关键字,有就需要分裂
			SplitChild(pNode, i, pNode->child[i]);
			if (key>pNode->key[i])//如果key比上移到父节点的元素大
				i++;
		}
		InsertNonFull(pNode->child[i], key);//已保证孩子结点关键字个数少于2*M-1个
	}
	
}

void insert(struct BTNode* &root, int key)
{
	struct BTNode* r = root;
	if (root->keyNum == 2 * M - 1){//根节点特殊处理,如果根节点关键字个数为2*M-1,
		struct BTNode* pNode = new BTNode();//新建一个结点作为新的根节点,并将现在的根节点作为
		root = pNode;//新根节点的孩子结点
		pNode->isLeaf = false;
		pNode->keyNum = 0;
		pNode->child[0] = r;
		SplitChild(pNode, 0, r);//孩子结点r有2*M-1个关键字
		InsertNonFull(pNode, key);
	}
	else
		InsertNonFull(r, key);
}

//按层级打印。
void PrintRow(struct BTNode* root)
{
	struct BTNode* pNode;
	queue<struct BTNode*> q;
	q.push(root);
	while (!q.empty()){
		pNode = q.front();
		q.pop();
		cout << "[ ";
		for (int i = 0; i < pNode->keyNum; i++)
			cout <<pNode->key[i] << " ";
		cout << "]" << endl;
		if (pNode->isLeaf)
			continue;
		for (int i = 0; i <= pNode->keyNum; i++)
			q.push(pNode->child[i]);
	}
}

void print(struct BTNode* root)
{
	if (root == NULL)
		return;
	cout << "[ ";
	for (int i = 0; i < root->keyNum; i++)
		cout << root->key[i] << " ";
	cout << "]" << endl;
	if (root->isLeaf)
		return;
	for (int i = 0; i <= root->keyNum; i++)
			print(root->child[i]);
}

//两个M-1个元素的结点合并
void merge(struct BTNode* parent, struct BTNode* pNode1, struct BTNode*pNode2, int index)
{
	pNode1->key[M - 1] = parent->key[index];
	for (int i = 0; i < M - 1; i++)//将pNode2的关键字移到pNode1中
		pNode1->key[i + M] = pNode2->key[i];
	pNode1->keyNum = 2 * M - 1;
	if (!pNode1->isLeaf){//如果不是叶子,将pNode2的孩子指针也移到pNode1中
		for (int i = 0; i < M; i++)
			pNode1->child[i + M] = pNode2->child[i];
	}

	for (int i = index; i < parent->keyNum; i++){//将父节点index以后的关键字以及孩子指针都向前移动一位
		parent->key[i] = parent->key[i + 1];
		parent->child[i+1] = parent->child[i + 2];
	}
	parent->keyNum--;
	delete pNode2;
}
//找到比pNode结点第一个关键字小的最大的关键字,也就是前继结点
int predecessor(struct BTNode* pNode)
{
	while (!pNode->isLeaf)
		pNode = pNode->child[pNode->keyNum];
	return pNode->key[pNode->keyNum - 1];
}
//找到后继结点
int successor(struct BTNode* pNode)
{
	while (!pNode->isLeaf)
		pNode = pNode->child[0];
	return pNode->key[0];
}

//pNode1向parent要一个结点key[index],parent向pNode0要一个结点,pNode1关键字个数为M-1
void ExchangeLeftNode(struct BTNode *parent, struct BTNode* pNode0, struct BTNode* pNode1, int index)
{
	for (int i = pNode1->keyNum; i > 0; i--)
		pNode1->key[i] = pNode1->key[i - 1];//pNode1结点所有关键字向后移动一位
	pNode1->key[0] = parent->key[index];//第0个关键字来自父节点
	pNode1->keyNum++;
	parent->key[index] = pNode0->key[pNode0->keyNum - 1];//父节点的index处的关键字来自pNode0的最大关键字
	
	if (!pNode0->isLeaf){//如果不是叶子结点,
		for (int i = pNode1->keyNum; i > 0; i--)//将pNode1的孩子指针都向后移动一位,并将pNode0的最后一个孩子指针赋给它的第一个
			pNode1->child[i] = pNode1->child[i - 1];
		pNode1->child[0] = pNode0->child[pNode0->keyNum];
	}

	pNode0->keyNum--;
}

void ExchangeRightNode(struct BTNode* parent, struct BTNode* pNode1, struct BTNode *pNode2, int index)
{
	
	pNode1->key[pNode1->keyNum] = parent->key[index];
	pNode1->keyNum++;
	parent->key[index] = pNode2->key[0];
	for (int i = 0; i < pNode2->keyNum - 1; i++)
		pNode2->key[i] = pNode2->key[i + 1];

	if (!pNode2->isLeaf){
		pNode1->child[pNode1->keyNum] = pNode2->child[0];
		for (int i = 0; i < pNode2->keyNum; i++)
			pNode2->child[i] = pNode2->child[i + 1];
	}
	pNode2->keyNum--;

}

//删除,结点关键字个数不少于M
void RemoveNonLess(struct BTNode* pNode, int key)
{
	if (pNode->isLeaf){//到了叶子结点,直接删除
		int i = 0;
		while (i<pNode->keyNum&&key>pNode->key[i])
			i++;
		if (i < pNode->keyNum&&key == pNode->key[i]){
			while (i < pNode->keyNum - 1){
				pNode->key[i] = pNode->key[i + 1];
				i++;
			}
			pNode->keyNum--;
		}
		else {
			cout << "not found!" << endl;
		}
	}
	else {
		int i = 0;
		while (i < pNode->keyNum&&key > pNode->key[i])//找到第一个大于等于key的关键字
			i++;
		if (i < pNode->keyNum&&key == pNode->key[i]){//在结点中找到要删除的关键字
			struct BTNode* pNode1 = pNode->child[i];
			struct BTNode* pNode2 = pNode->child[i + 1];
			if (pNode1->keyNum >= M){//如果关键字左边的孩子结点的关键字数大于等于M
				int target = predecessor(pNode1);//将其前继结点移到pNode中
				pNode->key[i] = target;
				RemoveNonLess(pNode1, target);//递归删除target
			}
			else if (pNode2->keyNum >= M){//右边,同理
				int target = successor(pNode2);
				pNode->key[i] = target;
				RemoveNonLess(pNode2, target);
			}
			else {
				merge(pNode, pNode1, pNode2, i);//都小于M,合并
				RemoveNonLess(pNode1, key);
			}
		}
		else {//不在此结点中
			struct BTNode *pNode1 = pNode->child[i];
			struct BTNode *pNode0 = NULL;
			struct BTNode *pNode2 = NULL;
			if (i>0)
				pNode0 = pNode->child[i - 1];//左结点
			if (i < pNode->keyNum)
				pNode2 = pNode->child[i + 1];//右结点
			if (pNode1->keyNum == M - 1){//如果要删除的孩子结点关键字个数为M-1
				if (i > 0 && pNode0->keyNum >= M){//如果左邻结点至少有M个关键字,向其借一个
					ExchangeLeftNode(pNode, pNode0, pNode1, i - 1);
				}
				else if (i < pNode->keyNum&&pNode2->keyNum >= M){//同理,
					ExchangeRightNode(pNode, pNode1, pNode2, i);
				}
				else if(i>0){//两个相邻结点都只有M-1个关键字,合并
					merge(pNode, pNode0, pNode1, i-1);
					pNode1 = pNode0;
				}
				else{
					merge(pNode, pNode1, pNode2, i);
				}
				RemoveNonLess(pNode1, key);
				     
			}
			else{
				RemoveNonLess(pNode1, key);
			}
		}
	}
}

void remove(struct BTNode* root, int key)
{
	if (root->keyNum == 1){//如果根节点只有两个孩子
		struct BTNode* pNode1 = root->child[0];
		struct BTNode* pNode2 = root->child[1];
		if (pNode1->keyNum == M - 1 && pNode2->keyNum == M - 1){//且两个孩子都只有M-1个关键字,合并
			merge(root, pNode1, pNode2, 0);
			delete root;
			root = pNode1;
		}
		else {
			RemoveNonLess(root, key);
		}
	}
	else {
		RemoveNonLess(root, key);
	}
}


int main(void)
{
	struct BTNode* root = create();
	srand(time(NULL));
	//for (int i = 0; i < 10000000; i++)
	//	insert(root, rand() % 97);
	//for (int i = 0; i < 100; i++)
		//remove(root, rand() % 97);
	insert(root, 'c');
	insert(root, 'n');
	insert(root, 'g');
	insert(root, 'a');
	insert(root, 'h');
	insert(root, 'e');
	insert(root, 'k');
	insert(root, 'q');
	insert(root, 'm');
	insert(root, 'f');
	
	insert(root, 'w');
	insert(root, 'l');
	insert(root, 't');
	insert(root, 'z');
	insert(root, 'd');
	insert(root, 'p');
	insert(root, 'r');
	insert(root, 'x');
	insert(root, 'y');
	insert(root, 's');
	remove(root, 'n');
	remove(root, 'b');
	PrintRow(root);
	return 0;
}



暂时没发现什么bug,如果发现了,请告知。

参考了《算法导论》以及http://blog.chinaunix.net/uid-20196318-id-3030529.html

b树的实现

标签:b树   数据结构   

原文地址:http://blog.csdn.net/u012637838/article/details/45461815

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!