1. 程式人生 > >T:B數的表示及其基本操作的實現(C++)

T:B數的表示及其基本操作的實現(C++)

習題內容:

1.掌握B樹的存貯結構。

2.實現B樹中關鍵字值的插入及刪除操作。

 

B樹的定義:

B樹也稱B-樹,它是一顆多路平衡查詢樹。我們描述一顆B樹時需要指定它的階數,階數表示了一個結點最多有多少個孩子結點,一般用字母M表示階數。當M取2時,就是我們常見的二叉搜尋樹。

一顆M階的B樹定義如下:

1)每個結點最多有M-1個關鍵字。

2)根結點最少可以只有1個關鍵字。

3)非根結點至少有Math.ceil(M / 2)-1個關鍵字。

4)每個結點中的關鍵字都按照從小到大的順序排列,每個關鍵字的左子樹中的所有關鍵字都小於它,而右子樹中的所有關鍵字都大於它。

5)所有葉子結點都位於同一層,或者說根結點到每個葉子結點的長度都相同。

 

程式碼實現:

/*測試環境:VS2017*/

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;

typedef struct _BTreeNode
{
	int key_count;
	int* key;
	bool leaf;
	_BTreeNode** child;
}BTreeNode, *PBTreeNode;

class BTree
{
private:
	PBTreeNode root;
	int t;//B樹的度,樹的階數為2t***m=2t,t=m/2

public:
	BTree(int m):root(NULL), t(m){}//構造
	~BTree(){DeleteTree(root);delete root;}//析構

	//基本操作:插入,刪除,列印,查詢
	void Insert(int key);
	void Delete(int key) { DeleteNonHalf(root, key); }
	void Display() { Print(root); }
	bool Search(int key);
private:
	PBTreeNode SearchBT(PBTreeNode pNode, int key);
	PBTreeNode AllocateNode();//劃分節點,初始化
	PBTreeNode UnionChild(PBTreeNode pParent, PBTreeNode pCLeft, PBTreeNode pCRight, int index);//結合兄弟節點
	int Max(PBTreeNode pNode);
	int Min(PBTreeNode pNode);
	void DeleteNonHalf(PBTreeNode pNode, int key);//刪除不足的節點
	void InsertNonfull(PBTreeNode pNode, int key);//插入未滿的節點
	void SplitChild(PBTreeNode pParent, int index, PBTreeNode pChild);//劃分子節點
	void DeallocateNode(PBTreeNode pNode);//解除劃分
	void DeleteTree(PBTreeNode pNode);//刪除樹
	void Print(PBTreeNode pNode);//列印樹
};

void BTree::Insert(int key)
{
	PBTreeNode r = root;
	if (r == NULL)
	{
		r = AllocateNode();
		r->leaf = true;
		r->key_count = 0;
		root = r;
	}

	if (r != NULL && r->key_count == (2 * t - 1))
	{
		PBTreeNode s = AllocateNode();
		root = s;
		s->leaf = false;
		s->key_count = 0;
		s->child[1] = r;
		SplitChild(s, 1, r);
		InsertNonfull(s, key);
	}
	else
	{
		InsertNonfull(r, key);
	}
	cout << "成功插入關鍵字:" << key << endl;
}

bool BTree::Search(int key)
{
	if (SearchBT(root, key))
		return false; 
	else 
		return true; 
}

PBTreeNode BTree::AllocateNode()
{
	PBTreeNode pTemp = new BTreeNode;
	pTemp->key = new int[2 * t];
	pTemp->child = new PBTreeNode[2 * t + 1];

	//初始化
	for (int i = 0; i < 2 * t; i++)
	{
		pTemp->key[i] = 0;
		pTemp->child[i] = NULL;
	}
	pTemp->child[2 * t] = NULL;

	return pTemp;
}

PBTreeNode BTree::SearchBT(PBTreeNode pNode, int key)
{
	int i = 1;
	while (i <= pNode->key_count && key > pNode->key[i])
		i++;
	
	if (i < pNode->key_count && key == pNode->key[i])
		return pNode->child[i];

	if (pNode->leaf)
		return NULL;
	else
		return SearchBT(pNode->child[i], key);
}

PBTreeNode BTree::UnionChild(PBTreeNode pParent, PBTreeNode pCLeft, PBTreeNode pCRight, int index)
{
	for (int i = 1; i < t; i++)
		pCLeft->key[t + i] = pCRight->key[i];
	pCLeft->key[t] = pParent->key[index];

	for (int i = 1; i <= t; i++)
		pCLeft->child[t + i] = pCRight->child[i];
	pCLeft->key_count = 2 * t - 1;

	for (int i = index; i < pParent->key_count; i++)
		pParent->key[i] = pParent->key[i + 1];

	for (int i = index + 1; i <= pParent->key_count; i++)
		pParent->child[i] = pParent->child[i + 1];
	pParent->key_count--;

	DeallocateNode(pCRight);

	if (pParent->key_count == 0)
	{
		DeallocateNode(root);
		root = pCLeft;
	}

	return pCLeft;
}

int BTree::Max(PBTreeNode pNode)
{
	while (!pNode->leaf)
	{
		pNode = pNode->child[pNode->key_count + 1];
	}

	return pNode->key[pNode->key_count];
}

int BTree::Min(PBTreeNode pNode)
{
	while (!pNode->leaf)
	{
		pNode = pNode->child[1];
	}

	return pNode->key[1];
}

void BTree::DeleteNonHalf(PBTreeNode pNode, int key)
{
	int i = 1;

	while (i <= pNode->key_count && key > pNode->key[i])
		i++;

	if (pNode->leaf)
	{
		if (i <= pNode->key_count && key == pNode->key[i])
		{
			for (int j = i; j < pNode->key_count; j++)
				pNode->key[j] = pNode->key[j + 1];
			pNode->key_count--;
			cout << "成功刪除關鍵字:" << key << endl;
			return;
		}
		else
		{
			//cout << "未能找到關鍵字:" << key << endl;
			return;
		}
	}

	if (i <= pNode->key_count && key == pNode->key[i])
	{
		if (pNode->child[i]->key_count >= t)
		{
			key = Max(pNode->child[i]);
			pNode->key[i] = key;

			DeleteNonHalf(pNode->child[i], key);
		}
		else if (pNode->child[i + 1]->key_count >= t)
		{
			key = Min(pNode->child[i + 1]);
			pNode->key[i] = key;

			DeleteNonHalf(pNode->child[i + 1], key);
		}
		else
		{
			PBTreeNode pChild = UnionChild(pNode, pNode->child[i], pNode->child[i + 1], i);

			DeleteNonHalf(pChild, key);
		}
	}
	else if (pNode->child[i]->key_count == t - 1)
	{
		if (i > 1 && pNode->child[i - 1]->key_count >= t)
		{
			PBTreeNode pMidNode = pNode->child[i];
			PBTreeNode pPreNode = pNode->child[i - 1];

			int nPreNodeKeyCount = pPreNode->key_count;

			for (int j = pMidNode->key_count + 1; j > 1; j--)
				pMidNode->key[j] = pMidNode->key[j - 1];
			pMidNode->key[1] = pNode->key[i - 1];

			for (int j = pMidNode->key_count + 2; j > 1; j--)
				pMidNode->child[j] = pMidNode->child[j - 1];
			pMidNode->child[1] = pPreNode->child[nPreNodeKeyCount + 1];
			pMidNode->key_count++;

			pNode->key[i - 1] = pPreNode->key[nPreNodeKeyCount];

			pPreNode->key[nPreNodeKeyCount] = 0;
			pPreNode->key[nPreNodeKeyCount + 1] = NULL;
			pPreNode->key_count--;

			DeleteNonHalf(pMidNode, key);
		}
		else if (i <= pNode->key_count && pNode->child[i + 1]->key_count >= t)
		{
			PBTreeNode pMidNode = pNode->child[i];
			PBTreeNode pNextNode = pNode->child[i + 1];

			int nNextNodeKeyCount = pNextNode->key_count;
			int nMidNodeKeyCount = pMidNode->key_count;

			pMidNode->key[nMidNodeKeyCount + 1] = pNode->key[i];
			pMidNode->child[nMidNodeKeyCount + 2] = pNextNode->child[1];
			pMidNode->key_count++;

			pNode->key[i] = pNextNode->key[1];

			for (int j = 1; j < nNextNodeKeyCount; j++)
				pNextNode->key[j] = pNextNode->key[j + 1];
			for (int j = 1; j <= nNextNodeKeyCount; j++)
				pNextNode->child[j] = pNextNode->child[j + 1];
			pNextNode->key_count--;

			DeleteNonHalf(pMidNode, key);
		}
		else
		{
			if (i > pNode->key_count)//當i指向最後一個關鍵字時,合併時往前移動一步
				i--;

			PBTreeNode pChild = UnionChild(pNode, pNode->child[i], pNode->child[i + 1], i);

			DeleteNonHalf(pChild, key);
		}
	}

	DeleteNonHalf(pNode->child[i], key);
}

void BTree::DeallocateNode(PBTreeNode pNode)
{
	delete[] pNode->key;
	delete[] pNode->child;
	delete pNode;
}

void BTree::SplitChild(PBTreeNode pParent, int index, PBTreeNode pChild)
{
	PBTreeNode pChildEx = AllocateNode();
	pChildEx->leaf = pChild->leaf;
	pChildEx->key_count = t - 1;

	for (int j = 1; j < t; j++)
		pChildEx->key[j] = pChild->key[j + t];

	if (!pChild->leaf)
		for (int j = 1; j <= t; j++)
			pChildEx->child[j] = pChild->child[j + t];

	pChild->key_count = t - 1;

	for (int j = pParent->key_count + 1; j > index; j--)
		pParent->child[j + 1] = pParent->child[j];
	pParent->child[index + 1] = pChildEx;

	for (int j = pParent->key_count; j >= index; j--)
		pParent->key[j + 1] = pParent->key[j];
	pParent->key[index] = pChild->key[t];
	pParent->key_count++;
}

void BTree::InsertNonfull(PBTreeNode pNode, int key)
{
	int i = pNode->key_count;

	if (pNode->leaf)
	{
		while (i >= 1 && key < pNode->key[i])
		{
			pNode->key[i + 1] = pNode->key[i];
			i--;
		}

		pNode->key[i + 1] = key;
		pNode->key_count++;
	}
	else
	{
		while (i >= 1 && key < pNode->key[i])
			i--;
		i++;

		if (pNode->child[i]->key_count == (2*t - 1))
		{
			SplitChild(pNode, i, pNode->child[i]);

			if (key > pNode->key[i])
				i++;
		}

		InsertNonfull(pNode->child[i], key);
	}
}

void BTree::DeleteTree(PBTreeNode pNode)//最後的根元素沒有被刪除,但是根裡的關鍵字陣列和孩子指標陣列已經刪除
{
	if (pNode->leaf)
	{
		delete[] pNode->key;
		delete[] pNode->child;
	}
	else
	{
		for (int i = 1; i <= pNode->key_count + 1; i++)
		{
			DeleteTree(pNode->child[i]);
			delete pNode->child[i];
		}

		delete[] pNode->key;
		delete[] pNode->child;
	}
}

void BTree::Print(PBTreeNode pNode)
{
	if (pNode->leaf)
	{
		cout << "葉子節點數目及成員:" << pNode->key_count << "		";

		for (int i = 1; i <= pNode->key_count; i++)
		{
			cout << pNode->key[i] << "	";
		}
		cout << endl;
	}
	else
	{
		for (int i = 1; i <= pNode->key_count + 1; i++)
			Print(pNode->child[i]);

		cout << "內部節點數目及成員:" << pNode->key_count << "		";
		for (int i = 1; i <= pNode->key_count; i++)
		{
			cout << pNode->key[i] << "	";
		}
		cout << endl;
	}
}

int main()
{
	int num, n,data;
	cout << "請分別輸入關鍵字的個數及要建立的B樹的度數(中間用空格隔開):";
	cin >> num >> n;
	BTree BT(n);
	cout << "請輸入" << num << "個建立B樹所需的關鍵字(中間用空格隔開):";
	for (int i = num; i > 0; i--)
	{
		cin >> data;
		BT.Insert(data);
	}
	cout << "B樹建立成功!" << endl;
	BT.Display();
	cout << "****************************************************************************" << endl;
	cin.ignore(1000, '\n');
	
	char s,ss[100];
	while (1)
	{
		cout << "請輸入操作(I:插入,D:刪除;以 ‘#’結束;多個數據間用空格隔開):" << endl;
		cin >> s;
		if (s == 'I')
		{
			while (cin >> ss && ss[0] != '#')
			{
				data = atoi(ss);
				/*if (BT.Search(data))
				{
					cout << "關鍵字" << data << "已存在!" << endl;
					continue;
				}*/
				BT.Insert(data);
			}
			BT.Display();
			cout << "****************************************************************************" << endl;
		}
		else if (s == 'D')
		{
			while (cin >> ss && ss[0] != '#')
			{
				data = atoi(ss);
				/*if (BT.Search(data)==false)
				{
					cout << "未能找到關鍵字:" << data << endl;
					continue;
				}*/
				BT.Delete(data);
				//cout << "成功刪除關鍵字:" << data << endl;
			}
			BT.Display();
			cout << "****************************************************************************" << endl;
		}
		else
			cout << "輸入錯誤!" << endl;
	}
	return 0;
}


/*
test data :

10 2
9 4 6 10 22 8 1 2 3 5 7 11 33 44 55 66 77 88 99

I 101 1001 111 1010 33 #
D 8 33 10 #

*/