1. 程式人生 > >B樹 (插入操作)

B樹 (插入操作)

B樹

B樹的特性

一顆m階的B樹滿足一下特性

(一)  樹中的每個結點至多有m顆子樹,至少有顆子樹。(除根結點和葉子結點外),其中表示m/2向上取整。()

(二)  樹中的每個結點至少有個關鍵字,至多m-1個關鍵字。

當結點的關鍵字個數滿時,那麼該結點就需要分裂,如何分裂?

假設*p結點已經有m-1個關鍵字,當再插入一個關鍵字後,其關鍵字個數為m,超標了!分裂開始,將*p結點分裂成兩個結點,分別是*p,*。其中*p結點有個關鍵字,分別為,其中C表示孩子指標,K表示關鍵字,下表是個數。那麼*結點有個關鍵字,分別為。關鍵字和指標*一起插入到*p的雙親結點中,如下圖所示。


假設是4階B樹,*p結點已經有3個關鍵字,當再插入一個關鍵字Q時,*p結點關鍵字個數超標,需要分裂,分裂結果如下。


分析,因為是4階B樹,所以。

其中*p結點有=1個關鍵字,對應為結點H。

*結點有個關鍵字,對應N,Q。

關鍵字和指標*一起插入到*p的雙親結點中。

再通俗點講就是,當在葉子結點插入一個元素時,判斷葉子結點空間是否已滿?

1.       當葉子結點空間未滿時,只需要將葉子結點中關鍵字大於插入元素的位置都向後移動一位,留下的那個空位就放這個新插入的元素。

2.       當葉子結點空間已滿時,就需要分裂,將該葉子結點個元素分裂到其相鄰的右結點中,第個元素上跳到雙親結點中,並在適當的位置插入,當然,插入後雙親結點原先的元素及指標位置都要往後移動一位。(如果,此時雙親結點的空間也滿了,那麼也要做分裂操作)。

3.       如果是在根結點中插入元素,跟結點的空間滿了,那麼需要將第個元素上跳給新的根結點,這樣樹的高度就增加1。

// B-Tree.cpp : Defines the entry point for the console application.
/*-----CODE FOR FUN--------------- 
-------CREATED BY Dream_Whui------ 
-------2015-3-22-------------------------*/  
//B樹(插入及遍歷)

#include "stdafx.h"
#include <iostream>

typedef int KeyType;		//定義鍵值型別

#define ORDER		4		//階數
#define BTree_D	2		//[OREDR/2]向上取整

typedef struct BTNode
{
	int keynum;					//鍵的個數
	KeyType key[ORDER-1];//結點關鍵字個數[BTree_D-1,ORDER-1]
	BTNode *child[ORDER];//結點孩子數[BTree_D,ORDER](除根節點與葉子節點)
	bool isLeaf;					//是否為孩子結點
}BTNode, *BTree;

void Tree_split_child(BTree &parent, int index, BTree &node);

void BTree_insert_nonfull(BTree &node, KeyType key)//在node中插入關鍵字key
{
	int i;
	if(node->isLeaf)			//若是葉子結點,則直接插入
	{
		i = node->keynum-1;
		while(i>=0 && key<node->key[i])
		{
			node->key[i+1] = node->key[i];
			i--;
		}
		node->key[i+1] = key;
		node->keynum++;
	}
	else									//不是葉子結點,先找到插入的位置i
	{
		i = node->keynum-1;
		while(i>=0 && key<node->key[i])
			i--;

		i++;
		if(node->child[i]->keynum == (ORDER-1))//判斷插入的位置i,其孩子結點的關鍵字個數是否已滿
		{
			Tree_split_child(node, i, node->child[i]);//滿了,則分裂
			if(key > node->key[i] )				//分裂完成後,重新判斷key與node->key[i]的大小,決定應該插入帶第i個孩子結點還是第i+1個孩子結點
				i++;
		}
		BTree_insert_nonfull(node->child[i], key);  
	}
}

void Tree_split_child(BTree &parent, int index, BTree &node)
{
	BTree newNode = (BTree)malloc(sizeof(BTNode));//產生一個新結點newNode,存放node的最後ORDER-BTree_D個關鍵字
	if(!newNode)
		return;
	newNode->isLeaf = node->isLeaf;	//newNode與node在同一層,因此兩者葉子節點屬性相同
	newNode->keynum = BTree_D -1;	//新結點關鍵字個數為BTree_D -1
	for(int i=0; i<ORDER; i++)					//新結點的孩子初始化為空
			newNode->child[i] = NULL;
	int i;
	for(i=0; i<newNode->keynum; i++)	//賦值新結點的關鍵字為node最後ORDER-BTree_D個關鍵字
	{									
		newNode->key[i] = node->key[BTree_D + i];
		node->key[BTree_D + i] = 0;
	}
	if(!node->isLeaf)		//如果不是葉子結點,是內部結點
	{
		for(i=0; i<BTree_D; i++)//
		{
			newNode->child[i] = node->child[BTree_D + i];
			node->child[BTree_D + i] = NULL;
		}
	}
	node->keynum = BTree_D -1;	//node關鍵字個數為BTree_D -1
	for (i = parent->keynum; i > index; --i) //因為不是在parent末尾插入關鍵字,因此從第i個孩子開始都網後移動一個位置
	{  
        parent->child[i + 1] = parent->child[i];  
    }
	parent->child[index+1] = newNode;//因為第index個孩子發生了分裂,因此第index+1個孩子指向新的結點

	for (i = parent->keynum - 1; i >= index; --i) //同理,parent從第index個關鍵字也往後移動一個位置
	{  
        parent->key[i + 1] = parent->key[i];  
    }  

	parent->key[index] = node->key[BTree_D-1];//移動完後,第index個位置上的元素為node的第BTree_D-1關鍵字
	parent->keynum++;

	node->key[BTree_D-1] = 0;
}

void BTree_Insert(BTree &tree, KeyType key)//在B樹中插入關鍵字key
{
	BTree node;
	BTree root = tree;
	if(NULL == root)			//根節點空,即第一次插入關鍵字
	{
		root = (BTree)malloc(sizeof(BTNode));
		if(!root)
			return;
		root->keynum = 1;			//鍵個數為1
		root->isLeaf = true;			//根節點又是葉子節點
		root->key[0] = key;			//第一個鍵值為key
		for(int i=0; i<ORDER; i++) //它的孩子結點初始化為空
			root->child[i] = NULL;
		tree = root;
		return;
	}

	if(root->keynum == (ORDER-1))	//根結點的關鍵字個數已滿,即要分裂
	{
		node = (BTree)malloc(sizeof(BTNode));//產生一個新結點,作為根結點
		if(!node)
			return;
		tree = node;
		node->keynum = 0;		//新的根節點關鍵字個數初始化為0個
		node->isLeaf = false;	//非葉子節點
		node->child[0] = root;	//其左孩子是原先的根結點

		Tree_split_child(node, 0, root);//分裂
		BTree_insert_nonfull(node, key);
	}
	else												//根結點的關鍵字個數未滿
		BTree_insert_nonfull(root, key);
}

void Tree_Print(BTree tree, int layer=1)//B樹遍歷
{
	BTree node = tree;
	int i;
	if(node)
	{
		printf("第 %d 層, %d node : ", layer, node->keynum);  
		for(i=0; i<node->keynum; i++)
		{
			printf("%d", node->key[i]);
		}
		printf("\n");
		
		layer++;
		for(i=0; i<=node->keynum; i++)
		{
			if(node->child[i])
				Tree_Print(node->child[i], layer);
		}
	}
	else
	{
		printf("樹為空。\n");  
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	BTree tree = NULL;
	BTree_Insert(tree,1);
	BTree_Insert(tree,2);
	BTree_Insert(tree,3);
	BTree_Insert(tree,4);
	BTree_Insert(tree,5);
	BTree_Insert(tree,6);
	BTree_Insert(tree,7);
	BTree_Insert(tree,8);
	BTree_Insert(tree,9);
	BTree_Insert(tree,10);
	Tree_Print(tree);
	return 0;
}