1. 程式人生 > >《演算法導論》中BTree的程式碼實現~

《演算法導論》中BTree的程式碼實現~

BTree.h

#ifndef _BTREE_H 
#define _BTREE_H

#define MINDEGREE 3 // 定義BTree的最小度
#define MAXDEGREE (MINDEGREE*2)

// 定義BTree的資料結構
typedef void* NodeData;

typedef struct _targBTreeNode
{
	NodeData	data; // 
	_targBTreeNode*	cs[MAXDEGREE]; //孩子指標陣列
	int	keys[MAXDEGREE-1]; //關鍵字陣列
	int count; // 關鍵字個數
	int is_leaf; // 是否為葉子結點
} BTreeNode, *BTree;

#define BTREE_SIZE sizeof(BTreeNode)

void alloc_tree(BTree &tree); //分配記憶體 
void insert_keys_to_tree(BTree &tree, int keys[], int n); //將keys插入至tree中
void insert_key_to_tree(BTree &tree, int key); // 將key插入至根樹tree中
void insert_key_to_unfull_tree(BTree &tree, int key); // 將key關鍵字插入至未滿子樹tree中 
void break_tree_child(BTree &tree, BTree &child, int i); // 將tree的第i個子孩子分裂
void disk_read(const BTree tree); // 代表讀磁碟,未實現
void disk_write(const BTree tree); // 代表寫磁碟,未實現
void display_tree(const BTree tree); // 顯示BTree
int search_tree(const BTree tree, int key); // 搜尋BTree中是否存在關鍵字
void delete_tree(BTree &tree, int key); // 刪除BTree中的某一關鍵字,主要處理BTree為空的兩種情況~
void delete_unless_tree(BTree &tree, int key); // 刪除BTree中的某一子樹~
int pre_succor_tree(const BTree tree, int i); // 找tree中第i位關鍵字的前驅~
int after_succor_tree(const BTree tree, int i); // 找Btree中第i位關鍵字的後繼~

#endif


BTree.cpp

#include "BTree.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <assert.h>

void alloc_tree(BTree &tree)
{
	if(0 == (tree = (BTree) malloc (BTREE_SIZE)))
	{
		exit(-1);
	}
	memset(tree, 0, BTREE_SIZE);
	tree->is_leaf = 1;
}

void insert_keys_to_tree(BTree &tree, int keys[], int n)
{
	for(int i = 0; i < n; i++)
	{
		insert_key_to_tree(tree, keys[i]);
	}
}

void insert_key_to_tree(BTree &tree, int key)
{
	if(tree == NULL)
	{
		alloc_tree(tree);
	}
	// 如果根關鍵字已滿,則進行分裂
	if(tree->count == (MAXDEGREE-1))
	{
		BTree new_root = NULL;
		alloc_tree(new_root);
		new_root->is_leaf = 0;
		new_root->cs[0] = tree;
		break_tree_child(new_root, tree, 0); // 分裂
		tree = new_root;
		insert_key_to_unfull_tree(tree, key);
	}
	else
	{
		insert_key_to_unfull_tree(tree, key);
	}
}

void break_tree_child(BTree &tree, BTree &child, int i)
{
	int j = 0;
	BTree rchild = NULL;

	alloc_tree(rchild);
	rchild->is_leaf = child->is_leaf; // 和原child一致
	// 將原child分成兩個孩子
	// 將原child的右側r-1個關鍵字移至rchild的keys中
	for(j = 0; j < (MINDEGREE-1); j++)
	{
		rchild->keys[j] = child->keys[MINDEGREE+j];
	}
	rchild->count = MINDEGREE - 1;
	// 將原child的右側的r+1個孩子移至rchild的cs中
	for(j = 0; j < MINDEGREE; j++)
	{
		rchild->cs[j] = child->cs[MINDEGREE+j];
	}
	child->count = MINDEGREE - 1;
	
	//將child的第r個結點關鍵字插入tree的第i個位置
	// 將tree從第i+1至第(tree->count)位置的cs後移一位
	for(j=tree->count;j > i; j--)
	{
		tree->cs[j+1] = tree->cs[j];
	}
	tree->cs[i+1] = rchild;
	// 將從第i至(tree->count-1)位置的關鍵字全部向後挪1
	for(j=tree->count-1; j > i; j--)
	{
		tree->keys[j+1] = tree->keys[j];
	}
	tree->keys[i] = child->keys[MINDEGREE-1];
	tree->count++;	
	disk_write(tree);
	disk_write(child);
	disk_write(rchild);
}

void insert_key_to_unfull_tree(BTree &tree, int key)
{
	int i = 0;
	
	// 如果是葉子結點,則直接插入即可,由於tree未全滿,則符合BTree定義
	if(tree->is_leaf == 1)
	{
		// 找到需要沿下插入的位置
		i=tree->count-1;
		while(i>=0 && key < tree->keys[i])
		{
			tree->keys[i+1] = tree->keys[i];
			i--;
		}
		tree->keys[i+1] = key;
		tree->count++;
		disk_write(tree);
	}
	else
	{ // 如果是內結點,則需找到繼續尋找位置後,判斷其子結點是否已經全滿,如全滿則分裂,並繼續沿下尋找
		i=tree->count-1;
		while(i>=0 && key < tree->keys[i])
		{
			i--;
		}
		i++; // 找到繼續尋找位置
		
		disk_read(tree->cs[i]);
		// 如果子樹已滿,則將其分裂
		if(tree->cs[i]->count == (MAXDEGREE-1))
		{
			break_tree_child(tree, tree->cs[i], i);
			if(key > tree->keys[i])
			i++;
		}
		insert_key_to_unfull_tree(tree->cs[i], key);
	}
}

void disk_read(const BTree tree)
{

}

void disk_write(const BTree tree)
{

}

void display_tree(const BTree tree)
{
	if(tree==NULL || tree->count == 0)
	{
		return;
	}

	printf("(%d", tree->keys[0]);
	int i = 0;
	for(i = 1; i < tree->count; i++)
	{
		printf(",%d", tree->keys[i]);
	}
	printf(")");
	if(0 == tree->is_leaf)
	{
		printf("(");
		for(i = 0; i < tree->count+1; i++)
		{
			display_tree(tree->cs[i]);
		}
		printf(")");
	}
}

int search_tree(const BTree tree, int key)
{
	if(tree==NULL)
		return 0;
	for(int i = tree->count-1; (i >= 0) && (key < tree->keys[i]); i--);
	// 在遍歷結束之前找到匹配關鍵字
	if((i != -1) && (key == tree->keys[i]))
	{
		return 1;
	}
	i++;

	if(1 == tree->is_leaf)
	{
		return 0;
	}
	else
	{
		return search_tree(tree->cs[i], key);
	}
}

void delete_tree(BTree &tree, int key)
{
	if(0 == search_tree(tree, key))
	{
		return;
	}
	
	// 查詢關鍵字位置,或子樹
	if( (tree->count==1) && (1 == tree->is_leaf))
	{
		free(tree);
		tree = NULL;
		return;
	}
	else if((tree->count==1) && (tree->keys[0] == key) && (tree->cs[0]->count == MINDEGREE-1) && (tree->cs[1]->count == MINDEGREE-1))
	{
		int j = 0;
		BTree l_child = tree->cs[0], r_child = tree->cs[1];
		
		l_child->keys[MINDEGREE-1] = tree->keys[0];
		for(j = 0; j < r_child->count; j++)
		{
			l_child->keys[j+MINDEGREE] = r_child->keys[j];
		}
		for(j = 0; j < MINDEGREE; j++)
		{
			l_child->cs[MINDEGREE+j] = r_child->cs[j];
		}
		l_child->count = MAXDEGREE - 1;
		free(r_child);
				
		free(tree);
		tree = l_child;
		delete_unless_tree(tree, key);
	}
	else
	{
		delete_unless_tree(tree, key);
	}
}

void delete_unless_tree(BTree &tree, int key)
{	
	// 查詢關鍵字位置,或子樹
	int i = 0, j = 0;
	for(i = tree->count-1; (i>=0) && (key < tree->keys[i]); i--);
	
	// 由於已經確保關鍵字在此樹中,且無子樹,故直接刪除
	if(1 == tree->is_leaf)
	{
		// 即將從第i+1位置至(tree->count-1)位置關鍵字都往前挪1位,並tree->count--
		for(j = i + 1; j < (tree->count); j++)
		{
			tree->keys[j-1] = tree->keys[j];
		}
		tree->count--;	
	}
	else if((i != -1) && (tree->keys[i] == key))
	{// 如果關鍵字在此樹當中,但為內結點,~
		// case2a 若其左孩子有大於等於r個結點,則將其前繼代替tree->key[i],並移至左孩子,將其遞迴刪除~
		if(tree->cs[i]->count >= MINDEGREE)
		{
			int new_key = pre_succor_tree(tree, i);
			tree->keys[i] = new_key;
			delete_unless_tree(tree->cs[i], new_key);
		}
		else if(tree->cs[i+1]->count >= MINDEGREE)
		{//case2b 若右孩子大於等於r個結點,則將其後繼代替tree->key[i], 並移至右孩子,遞迴刪除~
			int new_key = after_succor_tree(tree, i+1);
			delete_unless_tree(tree->cs[i+1], new_key);
		}
		else
		{//case2c 若左右孩子均為r-1個關鍵字,則需將刪除結點下沉並左右孩子合併,再刪除~
			BTree l_child = tree->cs[i], r_child = tree->cs[i+1];
			
			l_child->keys[MINDEGREE-1] = tree->keys[i];
			for(j = 0; j < r_child->count; j++)
			{
				l_child->keys[j+MINDEGREE] = r_child->keys[j];
			}
			for(j = 0; j < MINDEGREE; j++)
			{
				l_child->cs[MINDEGREE+j] = r_child->cs[j];
			}
			l_child->count = MAXDEGREE - 1;
			free(r_child);
			
			// 將keys域中(i+1)位至(tree->count-1)位向前移1位
			for(j = i+1; j < (tree->count -1); j++)
			{
				tree->keys[j-1] = tree->keys[j];
			}
			// 將cs域中從(i+2)位至(tree->count)位向前移1位
			for(j = i+2; j < tree->count; j++)
			{
				tree->cs[j-1] = tree->cs[j];
			}
			tree->count--;
			
			delete_unless_tree(tree->cs[i], key);
		}
	}
	else
	{ // 如果不在tree的keys中,且為內結點~
		i++; // 找至子孩子的正確位置
		if(tree->cs[i]->count >= MINDEGREE)
		{// 如果關鍵字個數>=最小度
			delete_unless_tree(tree->cs[i], key);
		}
		else
		{// 如果子樹根只有MINDEGREE-1個關鍵字,則需要考慮從兄弟借,或與兄弟合併情況
			// case 3a 如果可以從其左孩子借一關鍵字
			if((i != 0) &&(tree->cs[i-1]->count >= MINDEGREE))
			{
				BTree ichild = tree->cs[i], lchild = tree->cs[i-1];
				// 將i孩子的所有子孩子右移1,以便為lchild的最後一個子孩子移至第0位置
				for(j = tree->count; j >= 0; j--)
				{
					ichild->cs[j+1] = ichild->cs[j]; 
				}
				ichild->cs[0] = lchild->cs[lchild->count];
				// 將i孩子的所有關鍵字右移1,以便為tree中第(i-1)位置的key移至第1個位置
				for(j = tree->count-1; j >= 0; j--)
				{
					ichild->keys[j+1] = ichild->keys[j];
				}
				ichild->keys[0] = tree->keys[i-1];
				ichild->count++;
				
				// 將tree中第(i-1)位關鍵字替換成lchild中最右關鍵字
				tree->keys[i-1] = lchild->keys[lchild->count-1];
				
				// 將lchild中左孩子左移1
				for(j = 0; j < lchild->count; j++)
				{
					lchild->cs[j] = lchild->cs[j+1];
				}
				// 將lchild中左孩子關鍵字左移1
				for(j = 0; j < (lchild->count-1); j++)
				{
					lchild->keys[j] = lchild->keys[j+1];
				}
				lchild->count--;
				delete_unless_tree(ichild, key);
			}// if case 3a
			else if((i != tree->count) && tree->cs[i+1]->count >= MINDEGREE)
			{// case3a' 如果child i的右兄弟關鍵字夠“借”(即>=MINDEGREE)
				BTree ichild = tree->cs[i], rchild = tree->cs[i+1];
				// 將rchild的第0個子孩子移至ichild中子孩子最未位置中
				ichild->cs[ichild->count+1] = rchild->cs[0];
				// 將tree的第i個關鍵字加至ichild的關鍵字中
				ichild->keys[ichild->count] = tree->keys[i];
				ichild->count++;				
				
				// 將rchild的第0個關鍵字覆蓋至tree中
				tree->keys[i] = rchild->keys[0];

				// 將rchild的所有子孩子左移1
				for(j = 0; j < rchild->count; j++)
				{
					rchild->cs[j] = rchild->cs[j+1];
				}
				for(j = 0; j < (rchild->count-1); j++)
				{
					rchild->keys[j] = rchild->keys[j+1];
				}
				rchild->count--;
				delete_unless_tree(ichild, key);
			}// else if
			else
			{// case3b 左右兄弟都為MINDEGREE-1的情況
				// 如沒有左兄弟,則將i+1,轉至有左兄弟的情況進行處理
				if(i == 0)
				{
					i++; 
				}

				BTree ichild = tree->cs[i];
				BTree lchild = tree->cs[i-1];
				// 將i的子孩子移至左兄弟
				for(j = 0; j < MINDEGREE; j++)
				{
					lchild->cs[j+MINDEGREE] = ichild->cs[j];
				}
				// 將tree的關鍵字i-1移至左兄弟中
				lchild->keys[MINDEGREE-1] = tree->keys[i-1];
				// 將i的關鍵字都移至左兄弟中
				for(j = 0; j < MINDEGREE-1; j++)
				{
					 lchild->keys[MINDEGREE+j] = ichild->keys[j];
				}
				lchild->count = MAXDEGREE-1;
				free(ichild);
				
				// 調整tree中的子孩子位置
				for(j=i; j < tree->count; j++)
				{
					tree->cs[j] = tree->cs[j+1];
				}
				// 調整tree中關鍵字位置
				for(j=i-1; j < (tree->count-1); j++)
				{
					tree->keys[j] = tree->keys[j+1];
				}
				tree->count--;
				delete_unless_tree(lchild, key);
			}
		}// else
	}// else
}

int pre_succor_tree(const BTree tree, int i)
{
	BTree p = tree->cs[i];
	//前驅為tree的最左子樹的最右葉子結點的最後右一個關鍵字
	while((p->is_leaf == 0))
	{
		p = p->cs[p->count];
	}
	return p->keys[p->count-1];
}

int after_succor_tree(const BTree tree, int i)
{
	BTree p = tree->cs[i+1];	
	//後繼為tree的右子樹最左葉子結點的第一個關鍵字
	while(p->is_leaf == 0)
	{
		p = p->cs[0];
	}
	return p->keys[0];
}
////////////////////////////////////
// 定義測試資料
#define TEST_NUM 11
int test_keys[TEST_NUM] = {10, 2, 1, 5, 4, 7, 8, 6, 13, 11, 14};

int main()
{
	BTree tree = NULL;

	insert_keys_to_tree(tree, test_keys, TEST_NUM);
	display_tree(tree);
	
	delete_tree(tree, 1);
	printf("\n\nafter delete 1:\n");
	display_tree(tree);

	delete_tree(tree, 7);
	printf("\n\nafter delete 7:\n");
	display_tree(tree);

	delete_tree(tree, 4);
	printf("\n\nafter delete 4:\n");
	display_tree(tree);

	delete_tree(tree, 2);
	printf("\n\nafter delete 2:\n");
	display_tree(tree);

	printf("\n");
	return 0;
}


執行截圖