B+Tree模板改進版0.51版——可獨立編譯,修改了錯誤
最近應網友的要求,仔細檢查和修改了B+Tree的模板類,修正了幾處錯誤,主要是刪除Key的引數錯誤,同時去掉了那個標頭檔案的包含,直接將標頭檔案的內容貼在了檔案開頭處,以使此模板類可以獨立使用。
註釋掉的顯示到樹控制元件中的方法,可以在使用MFC的CTreeCtrl情況下自行恢復即可,這樣就可以將整個B+Tree顯示到樹控制元件中,方便只管的觀察B+Tree節點的構造。但是因為B+Tree結構的特殊性,所以在檢視時可能需要花些心思來分析。
#include <tchar.h>
#define WIN32_LEAN_AND_MEAN //在windows.h中排除單獨引用的APIs,比如WinSock的API
#include <windows.h>
#pragma once
#define GRSPOSITION void*
//==========================================================
//TRank 表示每個節點中的階數,則節點中的Key數目是2*TRank
//指向下級的指標數目是 2*TRank + 1,同時Key和Data是一對一的
//要求Key型別必須有 > 運算子
//實際使用中最好為TKey和TData型別過載 = 運算子
//==========================================================
template <class TKey,class TData,INT TRank>
class CGRSBTreeNode
{
public:
INT m_nCnt; //當前節點中的Key個數
TKey m_Key [2 * TRank]; //Key陣列
TData m_Data[2 * TRank]; //Data陣列
CGRSBTreeNode<TKey,TData,TRank>* m_pChild[ 2 * TRank + 1 ]; //指向孩子的指標陣列
public:
CGRSBTreeNode()
:m_nCnt(0)
{
::ZeroMemory(m_Key, 2 * TRank * sizeof(TKey));
::ZeroMemory(m_Data,2 * TRank * sizeof(TData));
::ZeroMemory(m_pChild,( 2 * TRank + 1) * sizeof(CGRSBTreeNode<TKey,TData,TRank>*));
}
};
template <class TKey,class TData,INT TRank = 10>
class CGRSBTree
{
protected:
CGRSBTreeNode<TKey,TData,TRank>* m_pRoot; //樹根節點
INT m_iHeight; //樹高度
INT m_nKeyCnt; //鍵總數
INT m_nNodeCnt; //節點總數
protected:
INT m_nSearchIndex; // 查詢時找到的鍵在節點中的位置
TData m_InsData; // 與要插的鍵相對應的值
TKey m_InsKey; /// 要插入的鍵
INT m_iAccessLevel; // 當前訪問的節點所處的高度
CGRSBTreeNode<TKey,TData,TRank>* m_pNewNode; // 在節點分割的時候指向新建的節點
BOOL m_bFlag; // 節點增減標誌
public:
CGRSBTree()
:m_iHeight(0)
,m_nKeyCnt(0)
,m_nNodeCnt(0)
,m_nSearchIndex(0)
,m_iAccessLevel(0)
,m_pNewNode(NULL)
,m_bFlag(FALSE)
{
//申請一個0節點的樹根
m_pRoot = NULL;
}
virtual ~CGRSBTree()
{
}
protected:
void Insert(CGRSBTreeNode<TKey,TData,TRank>*& pNode,const TKey& Key,const TData& Data)
{
INT i = 0;
INT j = 0;
INT k = 0;
m_iAccessLevel --;
if ( m_iAccessLevel < 0 )
{
m_pNewNode = NULL;
m_InsKey = Key;
m_InsData = Data;
m_nKeyCnt ++;
m_bFlag = TRUE;
//pNode = NewNode(pNode,Key,Data);
return;
}
for(i = 0, j = pNode->m_nCnt - 1;
i < j;
k= ( j + i) / 2, ( Key > pNode-> m_Key[k])?( i = k + 1 ):( j = k ));
if ( Key == pNode-> m_Key[ i ] )
{
Error(1,Key); /* 鍵已經在樹中 */
m_bFlag = FALSE;
return;
}
if ( Key > pNode-> m_Key[ i ] )
{/* i == pNode->m_nCnt - 1 時有可能出現 */
i ++;
}
//向孩子節點插入
Insert( pNode -> m_pChild[ i ] , Key, Data );
if ( FALSE == m_bFlag )
{
return;
}
/* 有新鍵要插入到當前節點中 */
if ( pNode->m_nCnt < 2 * TRank )
{/* 當前節點未滿 */
InsertAt( pNode, i ,Key,Data); /* 把鍵值+子樹對插入當前節點中 */
m_bFlag = FALSE; /* 指示上層節點沒有需要插入的鍵值+子樹,插入過程結束 */
}
else
{/* 當前節點已滿,則分割這個頁面並把鍵值+子樹對插入當前節點中 */
SplitNode(pNode, i); /* 繼續指示上層節點把返回的鍵值+子樹插入其中 */
}
}
/*
* 把一個鍵和對應的右子樹插入一個節點中
*/
void InsertAt(CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex,const TKey& Key,const TData& Data)
{
int i = 0;
/* 把所有大於要插入的鍵值的鍵和對應的右子樹右移 */
for(i = pNode->m_nCnt; i > nIndex; i--)
{
pNode-> m_Key[ i ] = pNode-> m_Key[i-1];
pNode-> m_Data[ i ] = pNode-> m_Data[i-1];
pNode-> m_pChild[i+1] = pNode-> m_pChild[ i ];
}
/* 插入鍵和右子樹 */
pNode-> m_Key[ i ] = Key;
pNode-> m_Data[ i ] = Data;
pNode-> m_pChild[ i + 1 ] = m_pNewNode;
pNode-> m_nCnt ++;
}
/*
* 前件是要插入一個鍵和對應的右子樹,並且本節點已經滿
* 導致分割這個節點,插入鍵和對應的右子樹,
* 並向上層返回一個要插入鍵和對應的右子樹
*/
void SplitNode( CGRSBTreeNode<TKey,TData,TRank>*& pNode, INT nIndex )
{
INT i = 0;
INT j = 0;
CGRSBTreeNode<TKey,TData,TRank>* pTmpNode = NULL;
TKey tmpKey;
TData tmpData;
/* 建立新節點 */
pTmpNode = new CGRSBTreeNode<TKey,TData,TRank>();
/*
* +---+--------+-----------+---------+--------+---------+
* | 0 | ...... | TRank | TRank+1 | ...... |2*TRank-1|
* +---+--------+-----------+---------+--------+---------+
* |<- TRank+1 ->|<- TRank-1 ->|
*/
if ( nIndex > TRank )
{//要插入當前節點的右半部分
//把從 2*TRank-1 到 TRank+1 的 TRank-1 個鍵值+子樹對轉移到新節點中,
//並且為要插入的鍵值+子樹空出位置
for( i = 2 * TRank - 1, j = TRank - 1; i >= nIndex; i --,j --)
{
pTmpNode-> m_Key[j] = pNode-> m_Key[ i ];
pTmpNode-> m_Data[j] = pNode-> m_Data[ i ];
pTmpNode-> m_pChild[ j + 1 ] = pNode-> m_pChild[i + 1];
}
for(i = nIndex - 1, j = nIndex - TRank - 2; j >= 0; i--,j--)
{
pTmpNode-> m_Key[j] = pNode-> m_Key[ i ];
pTmpNode-> m_Data[j] = pNode-> m_Data[ i ];
pTmpNode-> m_pChild[ j + 1 ] = pNode-> m_pChild[i + 1];
}
//把節點的最右子樹轉移成新節點的最左子樹
pTmpNode-> m_pChild[0] = pNode-> m_pChild[ TRank + 1 ];
//在新節點中插入鍵和右子樹
pTmpNode-> m_Key[ nIndex - TRank - 1] = m_InsKey;
pTmpNode-> m_Data[ nIndex - TRank - 1] = m_InsData;
pTmpNode-> m_pChild[ nIndex - TRank ] = m_pNewNode;
//設定要插入上層節點的鍵和值
m_InsKey = pNode-> m_Key[TRank];
m_InsData = pNode-> m_Data[TRank];
//tmpKey = pNode->m_Key[TRank];
//tmpData = pNode->m_Data[TRank];
}
else
{ /* nIndex <= TRank */
/* 把從 2*TRank-1 到 TRank 的 TRank 個鍵值+子樹對轉移到新節點中 */
for( i = 2 * TRank - 1,j = TRank - 1; j >= 0; i--,j--)
{
pTmpNode-> m_Key[j] = pNode-> m_Key[ i ];
pTmpNode-> m_Data[j] = pNode-> m_Data[ i ];
pTmpNode-> m_pChild[j + 1] = pNode-> m_pChild[ i + 1 ];
}
if (nIndex == TRank)
{
pTmpNode-> m_pChild[0] = m_pNewNode;
}
else
{ /* (nIndex<TRank) 要插入當前節點的左半部分 */
/* 把節點當前的最右子樹轉移成新節點的最左子樹 */
pTmpNode-> m_pChild[0] = pNode-> m_pChild[ TRank ];
/* 儲存要插入上層節點的鍵和值 */
tmpKey = pNode-> m_Key[ TRank - 1];
tmpData = pNode-> m_Data[ TRank - 1];
/* 把所有大於要插入的鍵值的鍵和對應的右子樹右移 */
for(i = TRank - 1; i > nIndex; i --)
{
pNode-> m_Key[ i ] = pNode-> m_Key[i-1];
pNode-> m_Data[ i ] = pNode-> m_Data[i - 1];
pNode-> m_pChild[ i + 1 ] = pNode-> m_pChild[ i ];
}
pNode-> m_Key[nIndex] = m_InsKey;
pNode-> m_Data[nIndex] = m_InsData;
pNode-> m_pChild[nIndex+1] = m_pNewNode;
m_InsKey = tmpKey;
m_InsData = tmpData;
}
}
pNode->m_nCnt = TRank;
pTmpNode->m_nCnt= TRank;
m_pNewNode = pTmpNode;
m_nNodeCnt ++;
//pNode = NewNode(pNode,tmpKey,tmpData);
}
void Remove( CGRSBTreeNode<TKey,TData,TRank>* pNode,const TKey& Key,TData& Data)
{
INT i = 0;
INT j = 0;
INT k = 0;
CGRSBTreeNode<TKey,TData,TRank>* pLeft = NULL;
CGRSBTreeNode<TKey,TData,TRank>* pRight = NULL;
INT nTmpLevel = 0;
m_iAccessLevel -- ;
if ( m_iAccessLevel < 0 )
{
Error(0,Key); /* 在整個樹中未找到要刪除的鍵 */
m_bFlag = FALSE;
return;
}
for(i=0, j = pNode->m_nCnt - 1;
i < j;
k=( j + i ) / 2, ( Key > pNode-> m_Key[k] ) ? (i = k + 1):( j = k ));
if ( Key == pNode-> m_Key[ i ] )
{ /* 找到要刪除的鍵 */
Data = pNode-> m_Data[ i ];
if (m_iAccessLevel == 0)
{
DelFromNode(pNode,i);
m_nKeyCnt -- ;
m_bFlag = TRUE;
return;
}
else
{ /* 這個鍵位於非葉節點 */
nTmpLevel = m_iAccessLevel - 1;
/* 找到前驅節點 */
pRight = pNode-> m_pChild[ i ];
while (nTmpLevel > 0)
{
pRight = pRight->m_pChild[pRight->m_nCnt];
nTmpLevel--;
}
pNode-> m_Key[ i ] = pRight-> m_Key[pRight->m_nCnt-1];
pNode-> m_Data[ i ] = pRight-> m_Data[pRight->m_nCnt-1];
pRight-> m_Data[pRight->m_nCnt-1] = NULL;
//Key = pRight-> m_Key[ pRight->m_nCnt - 1 ];
}
}
else if (Key > pNode-> m_Key[ i ])
{/* i == pNode->m_nCnt-1 時有可能出現 */
i++;
}
Remove(pNode-> m_pChild[ i ], Key , Data );
/* 調整平衡 */
if ( FALSE == m_bFlag )
{
return;
}
if (pNode-> m_pChild[ i ]->m_nCnt < TRank)
{
if (i == pNode->m_nCnt)
{/* 在最右子樹中發生了刪除 */
i--; /* 調整最右鍵的左右子樹平衡 */
}
pLeft = pNode-> m_pChild [ i ];
pRight = pNode-> m_pChild[i+1];
if (pRight->m_nCnt > TRank)
{
MoveLeftNode(pNode,i);
}
else if(pLeft->m_nCnt > TRank)
{
MoveRightNode(pNode,i);
}
else
{
JoinNode(pNode,i);
/* 繼續指示上層節點本子樹的鍵數量減少 */
return;
}
m_bFlag = FALSE;
/* 指示上層節點本子樹的鍵數量沒有減少,刪除過程結束 */
}
}
/*
* 合併一個節點的某個鍵對應的兩個子樹
*/
void JoinNode(CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex)
{
CGRSBTreeNode<TKey,TData,TRank>* pLeft = NULL;
CGRSBTreeNode<TKey,TData,TRank>* pRight = NULL;
INT i = 0;
INT j = 0;
pLeft = pNode-> m_pChild[nIndex];
pRight = pNode-> m_pChild[nIndex + 1];
/* 把這個鍵下移到它的左子樹 */
pLeft-> m_Key[pLeft->m_nCnt] = pNode-> m_Key[nIndex];
pLeft-> m_Data[pLeft->m_nCnt] = pNode-> m_Data[nIndex];
/* 把右子樹中的所有鍵值和子樹轉移到左子樹 */
for (j=pRight->m_nCnt-1,i=pLeft->m_nCnt+pRight->m_nCnt; j >= 0 ; j--,i--)
{
pLeft-> m_Key[ i ] = pRight-> m_Key[j];
pLeft-> m_Data[ i ] = pRight-> m_Data[j];
pLeft-> m_pChild[ i ] = pRight-> m_pChild[j];
}
pLeft-> m_pChild[ pLeft->m_nCnt + pRight->m_nCnt + 1 ] = pRight-> m_pChild[ pRight->m_nCnt ];
pLeft->m_nCnt += pRight->m_nCnt + 1;
delete pRight;
/* 把這個鍵右邊的鍵和對應的右子樹左移 */
for (i=nIndex; i < pNode->m_nCnt-1; i++)
{
pNode-> m_Key[ i ] = pNode-> m_Key[i+1];
pNode-> m_Data[ i ] = pNode-> m_Data[i+1];
pNode-> m_pChild[i+1] = pNode-> m_pChild[i+2];
}
pNode->m_nCnt --;
m_nNodeCnt --;
}
/*
* 從一個鍵的右子樹向左子樹轉移一些鍵,使兩個子樹平衡
*/
void MoveLeftNode(CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex)
{
CGRSBTreeNode<TKey,TData,TRank>* pLeft = NULL;
CGRSBTreeNode<TKey,TData,TRank>* pRight = NULL;
INT k = 0; /* 應轉移的鍵的數目 */
INT i = 0;
INT j = 0;
pLeft = pNode-> m_pChild[nIndex];
pRight = pNode-> m_pChild[nIndex + 1];
k = ( pRight->m_nCnt - pLeft->m_nCnt ) / 2;
/* 把這個鍵下移到它的左子樹 */
pLeft-> m_Key[pLeft->m_nCnt] = pNode-> m_Key[nIndex];
pLeft-> m_Data[pLeft->m_nCnt] = pNode-> m_Data[nIndex];
/* 把右子樹的最左子樹轉移成左子樹的最右子樹
* 從右子樹向左子樹移動 k-1 個鍵+子樹對 */
for (j=k-2,i=pLeft->m_nCnt+k-1; j >= 0; j--,i--)
{
pLeft-> m_Key[ i ] = pRight-> m_Key[j];
pLeft-> m_Data[ i ] = pRight-> m_Data[j];
pLeft-> m_pChild[ i ] = pRight-> m_pChild[j];
}
pLeft-> m_pChild[ pLeft->m_nCnt + k ] = pRight-> m_pChild[ k - 1 ];
/* 把右子樹的最左鍵提升到這個鍵的位置上 */
pNode-> m_Key[nIndex] = pRight-> m_Key[k-1];
pNode-> m_Data[nIndex] = pRight-> m_Data[k-1];
/* 把右子樹中的所有鍵值和子樹左移 k 個位置 */
pRight-> m_pChild[0] = pRight-> m_pChild[k];
for (i=0; i<pRight->m_nCnt-k; i++)
{
pRight-> m_Key[ i ] = pRight-> m_Key[ i + k ];
pRight-> m_Data[ i ] = pRight-> m_Data[ i + k ];
pRight-> m_pChild[ i ] = pRight-> m_pChild[ i + k ];
}
pRight-> m_pChild[ pRight->m_nCnt - k ] = pRight-> m_pChild[pRight->m_nCnt];
pLeft->m_nCnt += k;
pRight->m_nCnt -= k;
}
/*
* 從一個鍵的左子樹向右子樹轉移一些鍵,使兩個子樹平衡
*/
void MoveRightNode( CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex)
{
CGRSBTreeNode<TKey,TData,TRank>* pLeft = NULL;
CGRSBTreeNode<TKey,TData,TRank>* pRight = NULL;
INT k = 0; /* 應轉移的鍵的數目 */
INT i = 0;
INT j = 0;
pLeft = pNode-> m_pChild[nIndex];
pRight = pNode-> m_pChild[nIndex + 1];
k = ( pLeft->m_nCnt - pRight->m_nCnt ) / 2;
/* 把右子樹中的所有鍵值和子樹右移 k 個位置 */
pRight-> m_pChild[ pRight->m_nCnt + k ] = pRight-> m_pChild[ pRight->m_nCnt ];
for ( i = pRight->m_nCnt - 1; i >= 0; i -- )
{
pRight-> m_Key[i + k] = pRight-> m_Key[ i ];
pRight-> m_Data[i + k] = pRight-> m_Data[ i ];
pRight-> m_pChild[ i + k ] = pRight-> m_pChild[ i ];
}
/* 把這個鍵下移到它的右子樹 */
pRight-> m_Key[k - 1] = pNode-> m_Key[nIndex];
pRight-> m_Data[k - 1] = pNode-> m_Data[nIndex];
/* 把左子樹的最右子樹轉移成右子樹的最左子樹 */
pRight-> m_pChild[k-1] = pLeft-> m_pChild[pLeft->m_nCnt];
/* 從左子樹向右子樹移動 k-1 個鍵+子樹對 */
for ( i = pLeft->m_nCnt - 1,j = k - 2; j >= 0; j --,i -- )
{
pRight-> m_Key[j] = pLeft-> m_Key[ i ];
pRight-> m_Data[j] = pLeft-> m_Data[ i ];
pRight-> m_pChild[j] = pLeft-> m_pChild[ i ];
}
/* 把左子樹的最右鍵提升到這個鍵的位置上 */
pNode-> m_Key[nIndex] = pLeft-> m_Key[ i ];
pNode-> m_Data[nIndex] = pLeft-> m_Data[ i ];
pLeft->m_nCnt -= k;
pRight->m_nCnt += k;
}
/*
* 把一個鍵和對應的右子樹從一個節點中刪除
*/
void DelFromNode(CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex)
{
INT i = 0;
/* 把所有大於要刪除的鍵值的鍵左移 */
for( i = nIndex; i < pNode->m_nCnt - 1; i ++ )
{
pNode-> m_Key[ i ] = pNode-> m_Key[i+1];
pNode-> m_Data[ i ] = pNode-> m_Data[i+1];
}
pNode->m_nCnt --;
}
/*
* 建立有兩個子樹和一個鍵的根節點
*/
CGRSBTreeNode<TKey,TData,TRank>* NewNode(CGRSBTreeNode<TKey,TData,TRank>* pNode,const TKey& Key,const TData& Data)
{
CGRSBTreeNode<TKey,TData,TRank>* pTmpNode = NULL;
pTmpNode = new CGRSBTreeNode<TKey,TData,TRank>();
pTmpNode-> m_nCnt = 1;
pTmpNode-> m_pChild[0] = pNode;
pTmpNode-> m_pChild[1] = m_pNewNode;
pTmpNode-> m_Key[0] = Key;
pTmpNode-> m_Data[0] = Data;
m_iHeight ++;
m_nNodeCnt ++;
return( pTmpNode );
}
/*
* 釋放根節點,並返回它的最左子樹
*/
CGRSBTreeNode<TKey,TData,TRank>* FreeNode(CGRSBTreeNode<TKey,TData,TRank>* pNode)
{
CGRSBTreeNode<TKey,TData,TRank>* pTmpNode = pNode-> m_pChild[0];
delete pNode;
m_iHeight --;
m_nNodeCnt --;
return pTmpNode;
}
void Error(int f,TKey Key)
{
//自定義的出錯處理程式碼
}
void DelAll(CGRSBTreeNode<TKey,TData,TRank>* pNode)
{
INT i = 0;
m_iAccessLevel --;
if ( m_iAccessLevel > 0 )
{
for ( i = 0; i <= pNode->m_nCnt ; i ++ )
{
DelAll( pNode-> m_pChild[ i ] );
}
}
delete pNode;
}
public:
BOOL Add(const TKey& Key,const TData& Data)
{
m_iAccessLevel = m_iHeight;
Insert( m_pRoot, Key, Data );
if ( m_bFlag )
{
m_pRoot = NewNode(m_pRoot,Key,Data); /* 樹的高度增加 */
}
return TRUE;
}
BOOL Del( const TKey& Key, TData& Data )
{
m_iAccessLevel = m_iHeight;
Remove( m_pRoot, Key, Data );
if ( 0 == m_pRoot->m_nCnt )
{
m_pRoot = FreeNode( m_pRoot );
}
return TRUE;
}
INT GetHeight()
{
return m_iHeight;
}
INT GetKeys()
{
return m_nKeyCnt;
}
double GetPayload()
{
if (m_nNodeCnt==0)
{
return 1;
}
return (double) m_nKeyCnt / (double)( m_nNodeCnt *( 2 * TRank ) );
}
CGRSBTreeNode<TKey,TData,TRank>* Destroy ()
{
m_iAccessLevel = m_iHeight;
m_iHeight = 0;
return Delall(m_pRoot);
}
public:
GRSPOSITION Search(const TKey& Key, TData& Data)
{
INT i = 0;
INT j = 0;
INT k = 0;
INT nLevel = m_iHeight;
CGRSBTreeNode<TKey,TData,TRank>* pNode = m_pRoot;
while ( nLevel >= 0 )
{
for( i = 0, j = pNode->m_nCnt - 1;
i < j;
k = ( j + i ) / 2, ( Key > pNode->m_Key[ k ] ) ? ( i = k + 1) : ( j = k ) );
if ( Key == pNode->m_Key [ i ] )
{//找到
Data = pNode->m_Data[i];
return (GRSPOSITION) &pNode->m_Key[i];
}
if ( Key > pNode->m_Key [ i ] )
{//節點在右側
i ++;
}
pNode = pNode->m_pChild[ i ];
nLevel --; //到下層查詢
}
return NULL;
}
CGRSBTreeNode<TKey,TData,TRank>* GetRoot()
{
return m_pRoot;
}
//void Show2Tree(CTreeCtrl*pTree,HTREEITEM hItem,CGRSBTreeNode<TKey,TData,TRank>*pNode)
//{
// if( NULL == pNode )
// {
// return;
// }
// HTREEITEM hTmp = NULL;
// CString strTmp;
// for( INT i = 0; i < pNode->m_nCnt; i ++ )
// {
// if( 0 == i )
// {
// hTmp = pTree->InsertItem(_T("Child(Start)"),hItem);
// Show2Tree(pTree,hTmp,pNode->m_pChild[0]);
// }
// strTmp.Format(_T("Key(%d)"),pNode->m_Key[i]);
// hTmp = pTree->InsertItem(strTmp,hItem);
// Show2Tree(pTree,hTmp,pNode->m_pChild[i+1]);
// }
//}
};