1. 程式人生 > >B樹的原理與實現(C++)

B樹的原理與實現(C++)

#pragma once
template<class T>
class CBTree
{
private:
	static const int M = 3;                  //B樹的最小度數
	static const int KEY_MAX = 2*M-1;        //節點包含關鍵字的最大個數
	static const int KEY_MIN = M-1;          //非根節點包含關鍵字的最小個數
	static const int CHILD_MAX = KEY_MAX+1;  //孩子節點的最大個數
	static const int CHILD_MIN = KEY_MIN+1;  //孩子節點的最小個數
	struct Node
	{
		bool isLeaf;             //是否是葉子節點
		int keyNum;              //節點包含的關鍵字數量
		T keyValue[KEY_MAX];     //關鍵字的值陣列
		Node *pChild[CHILD_MAX]; //子樹指標陣列

		Node(bool b=true, int n=0)
			:isLeaf(b), keyNum(n){}
	};
public:
	CBTree()
	{
		m_pRoot = NULL;  //建立一棵空的B樹
	}

	~CBTree()
	{
		clear();
	}

	bool insert(const T &key)    //向B數中插入新結點key
	{
		if (contain(key))  //檢查該關鍵字是否已經存在
		{
			return false;
		}
		else
		{
			if (m_pRoot==NULL)//檢查是否為空樹
			{
				m_pRoot = new Node();
			}
			if (m_pRoot->keyNum==KEY_MAX) //檢查根節點是否已滿
			{
				Node *pNode = new Node();  //建立新的根節點
				pNode->isLeaf = false;
				pNode->pChild[0] = m_pRoot;
				splitChild(pNode, 0, m_pRoot);
				m_pRoot = pNode;  //更新根節點指標
			}
			insertNonFull(m_pRoot, key);
			return true;
		}
	}

	bool remove(const T &key)    //從B中刪除結點key
	{
		if (!search(m_pRoot, key))  //不存在
		{
			return false;
		}
		if (m_pRoot->keyNum==1)//特殊情況處理
		{
			if (m_pRoot->isLeaf)
			{
				clear();
				return true;
			}
			else
			{
				Node *pChild1 = m_pRoot->pChild[0];
				Node *pChild2 = m_pRoot->pChild[1];
				if (pChild1->keyNum==KEY_MIN&&pChild2->keyNum==KEY_MIN)
				{
					mergeChild(m_pRoot, 0);
					deleteNode(m_pRoot);
					m_pRoot = pChild1;
				}
			}
		}
		recursive_remove(m_pRoot, key);
		return true;
	}
	void display()const //列印樹的關鍵字
	{
		displayInConcavo(m_pRoot,KEY_MAX*10);
	}
	bool contain(const T &key)const   //檢查該key是否存在於B樹中
	{
		return search(m_pRoot, key);
	}
	void clear()                      //清空B樹
	{
		recursive_clear(m_pRoot);
		m_pRoot = NULL;
	}
private:
	//刪除樹
	void recursive_clear(Node *pNode)
	{
		if (pNode!=NULL)
		{
			if (!pNode->isLeaf)
			{
				for(int i=0; i<=pNode->keyNum; ++i)
					recursive_clear(pNode->pChild[i]);
			}
			deleteNode(pNode);
		}
	}

	//刪除節點
	void deleteNode(Node *&pNode)
	{
		if (pNode!=NULL)
		{
			delete pNode;
			pNode = NULL;
		}
	}

	//查詢關鍵字
	bool search(Node *pNode, const T &key)const 
	{
		if (pNode==NULL)  //檢測節點指標是否為空,或該節點是否為葉子節點
		{
			return false;
		}
		else
		{
			int i;
			for (i=0; i<pNode->keyNum && key>*(pNode->keyValue+i); ++i)//找到使key<=pNode->keyValue[i]成立的最小下標i
			{
			}
			if (i<pNode->keyNum && key==pNode->keyValue[i])
			{
				return true;
			}
			else
			{
				if (pNode->isLeaf)   //檢查該節點是否為葉子節點
				{
					return false;
				}
				else
				{
					return search(pNode->pChild[i], key);
				}
			}
		}
	}

	//分裂子節點
	void splitChild(Node *pParent, int nChildIndex, Node *pChild)  
	{
		//將pChild分裂成pLeftNode和pChild兩個節點
		Node *pRightNode = new Node();//分裂後的右節點
		pRightNode->isLeaf = pChild->isLeaf;
		pRightNode->keyNum = KEY_MIN;
		int i;
		for (i=0; i<KEY_MIN; ++i)//拷貝關鍵字的值
		{
			pRightNode->keyValue[i] = pChild->keyValue[i+CHILD_MIN];
		}
		if (!pChild->isLeaf)  //如果不是葉子節點,拷貝孩子節點指標
		{
			for (i=0; i<CHILD_MIN; ++i)
			{
				pRightNode->pChild[i] = pChild->pChild[i+CHILD_MIN];
			}
		}
		
		pChild->keyNum = KEY_MIN;  //更新左子樹的關鍵字個數

		for (i=pParent->keyNum; i>nChildIndex; --i)//將父節點中的nChildIndex後的所有關鍵字的值和子樹指標向後移一位
		{
			pParent->pChild[i+1] = pParent->pChild[i];
			pParent->keyValue[i] = pParent->keyValue[i-1];
		}
		++pParent->keyNum;  //更新父節點的關鍵字個數
		pParent->pChild[nChildIndex+1] = pRightNode;  //儲存右子樹指標
		pParent->keyValue[nChildIndex] = pChild->keyValue[KEY_MIN];//把節點的中間值提到父節點
	}

	//在非滿節點中插入關鍵字
	void insertNonFull(Node *pNode, const T &key)
	{
		int i = pNode->keyNum;  //獲取節點內關鍵字個數
		if (pNode->isLeaf)      //pNode是葉子節點
		{
			while (i>0&&key<pNode->keyValue[i-1])   //從後往前,查詢關鍵字的插入位置
			{
				pNode->keyValue[i] = pNode->keyValue[i-1];  //向後移位
				--i;
			}
			pNode->keyValue[i] = key;  //插入關鍵字的值
			++pNode->keyNum; //更新節點關鍵字的個數
		}
		else//pNode是內節點
		{
			while(i>0&&key<pNode->keyValue[i-1])   //從後往前,查詢關鍵字的插入的子樹
				--i;
			Node *pChild = pNode->pChild[i];  //目標子樹結點指標 
			if (pChild->keyNum==KEY_MAX)  //子樹節點已滿
			{
				splitChild(pNode, i, pChild);//分裂子樹節點
				if(key>pNode->keyValue[i])   //確定目標子樹
					pChild = pNode->pChild[i+1];
			}
			insertNonFull(pChild, key);  //插入關鍵字到目標子樹節點
		}
	}

	//用括號列印樹
	void displayInConcavo(Node *pNode, int count)const
	{
		if (pNode!=NULL)
		{
			int i, j;
			for (i=0; i<pNode->keyNum; ++i)
			{
				if (!pNode->isLeaf)
				{
					displayInConcavo(pNode->pChild[i], count-2);
				}
				for (j=count; j>=0; --j)
				{
					cout<<"-";
				}
				cout<<pNode->keyValue[i]<<endl;
			}
			if (!pNode->isLeaf)
			{
				displayInConcavo(pNode->pChild[i], count-2);
			}
		}
	}

	//合併兩個子節點
	void mergeChild(Node *pParent, int index)
	{
		Node *pChild1 = pParent->pChild[index];
		Node *pChild2 = pParent->pChild[index+1];
		//將pChild2資料合併到pChild1
		pChild1->keyNum = KEY_MAX;
		pChild1->keyValue[KEY_MIN] = pParent->keyValue[index];//將父節點index的值下移
		int i;
		for (i=0; i<KEY_MIN; ++i)
		{
			pChild1->keyValue[i+KEY_MIN+1] = pChild2->keyValue[i];
		}
		if (!pChild1->isLeaf)
		{
			for (i=0; i<CHILD_MIN; ++i)
			{
				pChild1->pChild[i+CHILD_MIN] = pChild2->pChild[i];
			}
		}

		//父節點刪除index的key,index後的往前移一位
		--pParent->keyNum;
		for(i=index; i<pParent->keyNum; ++i)
		{
			pParent->keyValue[i] = pParent->keyValue[i+1];
			pParent->pChild[i+1] = pParent->pChild[i+2];
		}
		deleteNode(pChild2);  //刪除pChild2
	}

	//遞迴的刪除關鍵字
    void recursive_remove(Node *pNode, const T &key)
	{
		int i=0;
		while(i<pNode->keyNum&&key>pNode->keyValue[i])
			++i;
		if (i<pNode->keyNum&&key==pNode->keyValue[i])//關鍵字key在節點pNode中
		{
			if (pNode->isLeaf)//pNode是個葉節點
			{
				//從pNode中刪除k
				--pNode->keyNum;
				for (; i<pNode->keyNum; ++i)
				{
					pNode->keyValue[i] = pNode->keyValue[i+1];
				}
				return;
			}
			else//pNode是個內節點
			{
				Node *pChildPrev = pNode->pChild[i];//節點pNode中前於key的子節點
				Node *pChildNext = pNode->pChild[i+1];//節點pNode中後於key的子節點
				if (pChildPrev->keyNum>=CHILD_MIN)//節點pChildPrev中至少包含CHILD_MIN個關鍵字
				{
					T prevKey = getPredecessor(pChildPrev); //獲取key的前驅關鍵字
				    recursive_remove(pChildPrev, prevKey);
					pNode->keyValue[i] = prevKey;     //替換成key的前驅關鍵字
					return;
				}
				else if (pChildNext->keyNum>=CHILD_MIN)//節點pChildNext中至少包含CHILD_MIN個關鍵字
				{
					T nextKey = getSuccessor(pChildNext); //獲取key的後繼關鍵字
					recursive_remove(pChildNext, nextKey);
					pNode->keyValue[i] = nextKey;     //替換成key的後繼關鍵字
					return;
				}
				else//節點pChildPrev和pChildNext中都只包含CHILD_MIN-1個關鍵字
				{
					mergeChild(pNode, i);
					recursive_remove(pChildPrev, key);
				}
			}
		}
		else//關鍵字key不在節點pNode中
		{
			Node *pChildNode = pNode->pChild[i];//包含key的子樹根節點
			if (pChildNode->keyNum==KEY_MIN)//只有t-1個關鍵字
			{
				Node *pLeft = i>0 ? pNode->pChild[i-1] : NULL;  //左兄弟節點
				Node *pRight = i<pNode->keyNum ? pNode->pChild[i+1] : NULL;//右兄弟節點
				int j;
				if (pLeft&&pLeft->keyNum>=CHILD_MIN)//左兄弟節點至少有CHILD_MIN個關鍵字
				{
					//父節點中i-1的關鍵字下移至pChildNode中
					for (j=pChildNode->keyNum; j>0; --j)  
					{
						pChildNode->keyValue[j] = pChildNode->keyValue[j-1];
					}
					pChildNode->keyValue[0] = pNode->keyValue[i-1];
					
					if (!pLeft->isLeaf)  
					{
						for (j=pChildNode->keyNum+1; j>0; --j) //pLeft節點中合適的子女指標移植到pChildNode中
						{
							pChildNode->pChild[j] = pChildNode->pChild[j-1];
						}
						pChildNode->pChild[0] = pLeft->pChild[pLeft->keyNum];
					}
					++pChildNode->keyNum;
					pNode->keyValue[i] = pLeft->keyValue[pLeft->keyNum-1];//pLeft節點中的最大關鍵字上升到pNode中
					--pLeft->keyNum;
				}
				else if (pRight&&pRight->keyNum>=CHILD_MIN)//右兄弟節點至少有CHILD_MIN個關鍵字
				{
					//父節點中i的關鍵字下移至pChildNode中
					pChildNode->keyValue[pChildNode->keyNum] = pNode->keyValue[i];
					++pChildNode->keyNum;
					pNode->keyValue[i] = pRight->keyValue[0];//pRight節點中的最小關鍵字上升到pNode中
					--pRight->keyNum;
					for (j=0; j<pRight->keyNum; ++j)
					{
						pRight->keyValue[j] = pRight->keyValue[j+1];
					}
					if (!pRight->isLeaf)  
					{
						pChildNode->pChild[pChildNode->keyNum] = pRight->pChild[0];//pRight節點中合適的子女指標移植到pChildNode中
					    for (j=0; j<=pRight->keyNum; ++j)
					    {
							pRight->pChild[j] = pRight->pChild[j+1];
					    }
					}
				}
				//左右兄弟節點都只包含CHILD_MIN-1個節點
				else if (pLeft)//與左兄弟合併
				{
					mergeChild(pNode, i-1);
					pChildNode = pLeft;
				}
				else if (pRight)//與右兄弟合併
				{
					mergeChild(pNode, i);

				}
			}
			recursive_remove(pChildNode, key);
		}
	}

	T getPredecessor(Node *pNode)//找到前驅關鍵字
	{
		while (!pNode->isLeaf)
		{
			pNode = pNode->pChild[pNode->keyNum];
		}
		return pNode->keyValue[pNode->keyNum-1];
	}

	T getSuccessor(Node *pNode)//找到後繼關鍵字
	{
		while (!pNode->isLeaf)
		{
			pNode = pNode->pChild[0];
		}
		return pNode->keyValue[0];
	}
	
private:
	Node * m_pRoot;  //B樹的根節點
};