1. 程式人生 > >B樹和B樹的實現 B-Tree

B樹和B樹的實現 B-Tree

根據演算法導論的描述。B樹是一種特殊的平衡樹和查詢樹。其主要用於磁碟內資料的儲存和查詢。因此,B樹一般每個結點會比較大,包含許多資料關鍵字,最好佔一個頁面(page),這樣存取的時候直接存取一個結點的資料。

B樹的基本性質(M階):

1、B樹的每個葉子節點的高度均一致。

2、對於非空B樹,普通非根非葉節點至少有M-1個關鍵字(M個子女),至多2M-1個關鍵字(2M個子女)。根節點至少包含2個子女(因為根節點只有1個關鍵字時,要有兩個對應的孩子指標)。

3、結點內部的資料關鍵字是按從小到大排序的。

B樹的結點的結構(M階):

typedef struct btree_node
{
	int k[2*M-1];	//用於儲存資料關鍵字
	btree_node *p[2*M];	//用於儲存指向孩子的指標
	int num;	// 當前結點內資料關鍵字的個數, <=2*M-1
	bool is_leaf;	// 當前結點是否是葉子結點 true/false
}btree_node,*btree;

B樹操作上的特質:

1、插入新資料關鍵字總是要往葉子結點上插入。

2、插入操作所造成的B樹的擴張,使得樹的高度升高一定是在根節點增加高度的。(這是由插入操作的手段所決定。)

B樹結點的建立:

btree init_btree_node()
{
	btree_node *node = (btree_node*)malloc(sizeof(btree_node));
	if(node ==NULL)
		return NULL;
	for(int i=0;i<2*M-1;i++)
		node->k[i] = 0;
	for(int i=0;i<2*M;i++)
		node->p[i] = NULL;
	node->num = 0;
	node->is_leaf = true;
	return node;
}
在寫如何插入新的資料關鍵字之前,先考慮一個結點如何分裂:
//child結點滿了,一分為二: 0~M-2  M-1  M~2M-2 
//   1
//M-1 M-1
int split_child(btree parent, int pos, btree child)
{
	//create a new child node for the other M-1 keys
	btree_node *new_child = init_btree_node();
	if(new_child == NULL)
		return -1;
	new_child->is_leaf = child->is_leaf;
	new_child->num = M-1;
	for(int i=0;i<M-1;i++)
		new_child->k[i] = child->k[i+M];
	if(new_child->is_leaf == false)
		for(int i=0;i<M;i++)
			new_child->p[i] = child->p[i+M];

	//adjust the former child node
	child->num = M-1;

	//adjust parent node
	for(int i=parent->num; i>pos;i--)
		parent->p[i+1] = parent->p[i];
	parent->p[pos+1] = new_child; 

	for(int i=parent->num-1;i>=pos;i--)
		parent->k[i+1] = parent->k[i];
	parent->k[pos] = child->k[M-1];
	parent->num ++;

	return 1;
}


對B樹新關鍵字插入的實現是個特別的過程,從根結點向下追溯到葉子結點,途中遇到滿的結點需要分裂:

--對根節點執行btree_insert();

--如果根節點滿了,B樹需要升高一層;

--向其孩子結點插入,遇到滿結點需要分裂,逐步遞迴到葉子結點。

btree btree_insert(btree root, int target)
{
	if(root == NULL)
		return NULL;
	if(root->num == 2*M-1)
	{
		//create new root
		//the only way to add the height of btree 
		btree_node *node = init_btree_node();
		if(node == NULL)
			return NULL;
		node->is_leaf = false;
		node->p[0] = root;
		split_child(node, 0, root);
		btree_insert_nonfull(node, target);
		return node;
	}
	else
	{
		btree_insert_nonfull(root, target);
		return root;
	}
}

void btree_insert_nonfull(btree node, int target)
{
	if(node->is_leaf == true)
	{
		int pos = node->num;
		while(pos>=1 && target < node->k[pos-1])
		{
			node->k[pos] = node->k[pos-1];
			pos--;
		}
		node->k[pos] = target;
		node->num ++; 
	}
	else
	{
		int pos = node->num;
		while(pos>0 && target < node->k[pos-1])
			pos--;
		if(node->p[pos]->num == 2*M-1)
		{
			split_child(node, pos, node->p[pos]);
			if(target > node->k[pos])
				pos++;
		}
		btree_insert_nonfull(node->p[pos], target);
	}
}


B樹的刪除操作:

未完成,有待學習。