最近应网友的要求,仔细检查和修改了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 on
#define GRSPOSITION void*
//==========================================================
//TRank 表示每个节点中的阶数,则节点中的Key数目是2*TRank
//指向下级的指针数目是 2*TRank + 1,同时Key和Da
//要求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_Da
CGRSBTreeNode<TKey,TData,TRank>* m_pChild[ 2 * TRank + 1 ]; //指向孩子的指针数组
public:
CGRSBTreeNode()
:m_nCnt(0)
{
::ZeroMemory(m_Key, 2 * TRank * sizeof(TKey));
::ZeroMemory(m_Da
::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& Da
{
INT i = 0;
INT j = 0;
INT k = 0;
m_iAccessLevel --;
if ( m_iAccessLevel < 0 )
{
m_pNewNode = NULL;
m_InsKey = Key;
m_InsData = Da
m_nKeyCnt ++;
m_bFlag = TRUE;
//pNode = NewNode(pNode,Key,Da
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, Da
if ( FALSE == m_bFlag )
{
return;
}
/* 有新键要插入到当前节点中 */
if ( pNode->m_nCnt < 2 * TRank )
{/* 当前节点未满 */
InsertAt( pNode, i ,Key,Da
m_bFlag = FALSE; /* 指示上层节点没有需要插入的键值+子树,插入过程结束 */
}
else
{/* 当前节点已满,则分割这个页面并把键值+子树对插入当前节点中 */
SplitNode(pNode, i); /* 继续指示上层节点把返回的键值+子树插入其中 */
}
}
/*
* 把一个键和对应的右子树插入一个节点中
*/
void InsertAt(CGRSBTreeNode<TKey,TData,TRank>* pNode, INT nIndex,const TKey& Key,const TData& Da
{
int i = 0;
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for(i = pNode->m_nCnt; i > nIndex; i--)
{
pNode-> m_Key[ i ] = pNode-> m_Key[i-1];
pNode-> m_Da
pNode-> m_pChild[i+1] = pNode-> m_pChild[ i ];
}
/* 插入键和右子树 */
pNode-> m_Key[ i ] = Key;
pNode-> m_Da
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_Da
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_Da
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_Da
pTmpNode-> m_pChild[ nIndex - TRank ] = m_pNewNode;
//设置要插入上层节点的键和值
m_InsKey = pNode-> m_Key[TRank];
m_InsData = pNode-> m_Da
//tmpKey = pNode->m_Key[TRank];
//tmpData = pNode->m_Da
}
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_Da
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_Da
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for(i = TRank - 1; i > nIndex; i --)
{
pNode-> m_Key[ i ] = pNode-> m_Key[i-1];
pNode-> m_Da
pNode-> m_pChild[ i + 1 ] = pNode-> m_pChild[ i ];
}
pNode-> m_Key[nIndex] = m_InsKey;
pNode-> m_Da
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& Da
{
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 ] )
{ /* 找到要删除的键 */
Da
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_Da
pRight-> m_Da
//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 , Da
/* 调整平衡 */
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_Da
/* 把右子树中的所有键值和子树转移到左子树 */
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_Da
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_Da
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_Da
/* 把右子树的最左子树转移成左子树的最右子树
* 从右子树向左子树移动 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_Da
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_Da
/* 把右子树中的所有键值和子树左移 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_Da
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_Da
pRight-> m_pChild[ i + k ] = pRight-> m_pChild[ i ];
}
/* 把这个键下移到它的右子树 */
pRight-> m_Key[k - 1] = pNode-> m_Key[nIndex];
pRight-> m_Da
/* 把左子树的最右子树转移成右子树的最左子树 */
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_Da
pRight-> m_pChild[j] = pLeft-> m_pChild[ i ];
}
/* 把左子树的最右键提升到这个键的位置上 */
pNode-> m_Key[nIndex] = pLeft-> m_Key[ i ];
pNode-> m_Da
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_Da
}
pNode->m_nCnt --;
}
/*
* 建立有两个子树和一个键的根节点
*/
CGRSBTreeNode<TKey,TData,TRank>* NewNode(CGRSBTreeNode<TKey,TData,TRank>* pNode,const TKey& Key,const TData& Da
{
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_Da
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& Da
{
m_iAccessLevel = m_iHeight;
Insert( m_pRoot, Key, Da
if ( m_bFlag )
{
m_pRoot = NewNode(m_pRoot,Key,Da
}
return TRUE;
}
BOOL Del( const TKey& Key, TData& Da
{
m_iAccessLevel = m_iHeight;
Remove( m_pRoot, Key, Da
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& Da
{
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 ] )
{//找到
Da
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]);
// }
//}
};