📄 cpp2.cpp
字号:
// 后面的数据依次前移
for (int j = i; j < m_Count - 1; j++)
{
m_Datas[j] = m_Datas[j + 1];
}
m_Datas[j] = INVALID;
m_Count--;
// 返回成功
return true;
}
// 分裂叶子结点,把本叶子结点的后一半数据剪切到指定的叶子结点中
KEY_TYPE CLeafNode::Split(CNode* pNode)
{
// 把本叶子结点的后一半数据移到指定的结点中
int j = 0;
for (int i = ORDER_V + 1; i <= MAXNUM_DATA; i++)
{
j++;
pNode->SetElement(j, this->GetElement(i));
this->SetElement(i, INVALID);
}
// 设置好Count个数
this->SetCount(this->GetCount() - j);
pNode->SetCount(pNode->GetCount() + j);
// 返回新结点的第一个元素作为键
return pNode->GetElement(1);
}
// 结合结点,把指定叶子结点的数据全部剪切到本叶子结点
bool CLeafNode::Combine(CNode* pNode)
{
// 参数检查
if (this->GetCount() + pNode->GetCount() > MAXNUM_DATA)
{
return false;
}
for (int i = 1; i <= pNode->GetCount(); i++)
{
this->Insert(pNode->GetElement(i));
}
return true;
}
BPlusTree::BPlusTree()
{
m_Depth = 0;
m_Root = NULL;
m_pLeafHead = NULL;
m_pLeafTail = NULL;
}
BPlusTree::~BPlusTree()
{
ClearTree();
}
// 在树中查找数据
bool BPlusTree::Search(KEY_TYPE data, char* sPath)
{
int i = 0;
int offset = 0;
if (NULL != sPath)
{
(void)sprintf(sPath+offset, "The serach path is:");
offset+=19;
}
CNode * pNode = GetRoot();
// 循环查找对应的叶子结点
while (NULL != pNode)
{
// 结点为叶子结点,循环结束
if (NODE_TYPE_LEAF == pNode->GetType())
{
break;
}
// 找到第一个键值大于等于key的位置
for (i = 1; (data >= pNode->GetElement(i) )&& (i <= pNode->GetCount()); i++)
{
}
if (NULL != sPath)
{
(void)sprintf(sPath+offset, " %3d -->", pNode->GetElement(1));
offset+=8;
}
pNode = pNode->GetPointer(i);
}
// 没找到
if (NULL == pNode)
{
return false;
}
if (NULL != sPath)
{
(void)sprintf(sPath+offset, "%3d", pNode->GetElement(1));
offset+=3;
}
// 在叶子结点中继续找
bool found = false;
for (i = 1; (i <= pNode->GetCount()); i++)
{
if (data == pNode->GetElement(i))
{
found = true;
}
}
if (NULL != sPath)
{
if (true == found)
{
(void)sprintf(sPath+offset, " ,successed.");
}
else
{
(void)sprintf(sPath+offset, " ,failed.");
}
}
return found;
}
/* 在B+树中插入数据
插入数据首先要找到理论上要插入的叶子结点,然后分三种情况:
(1) 叶子结点未满。直接在该结点中插入即可;
(2) 叶子结点已满,且无父结点(即根结点是叶子结点),需要首先把叶子结点分裂,然后选择插入原结点或新结点,然后新生成根节点;
(3) 叶子结点已满,但其父结点未满。需要首先把叶子结点分裂,然后选择插入原结点或新结点,再修改父结点的指针;
(4) 叶子结点已满,且其父结点已满。需要首先把叶子结点分裂,然后选择插入原结点或新结点,接着把父结点分裂,再修改祖父结点的指针。
因为祖父结点也可能满,所以可能需要一直递归到未满的祖先结点为止。
*/
bool BPlusTree::Insert(KEY_TYPE data)
{
// 检查是否重复插入
bool found = Search(data, NULL);
if (true == found)
{
return false;
}
// for debug
//if (289 == data)
//{
// printf("\n%d,check failed!",data);
//}
// 查找理想的叶子结点
CLeafNode* pOldNode = SearchLeafNode(data);
// 如果没有找到,说明整个树是空的,生成根结点
if (NULL == pOldNode)
{
pOldNode = new CLeafNode;
m_pLeafHead = pOldNode;
m_pLeafTail = pOldNode;
SetRoot(pOldNode);
}
// 叶子结点未满,对应情况1,直接插入
if (pOldNode->GetCount() < MAXNUM_DATA)
{
return pOldNode->Insert(data);
}
// 原叶子结点已满,新建叶子结点,并把原结点后一半数据剪切到新结点
CLeafNode* pNewNode = new CLeafNode;
KEY_TYPE key = INVALID;
key = pOldNode->Split(pNewNode);
// 在双向链表中插入结点
CLeafNode* pOldNext = pOldNode->m_pNextNode;
pOldNode->m_pNextNode = pNewNode;
pNewNode->m_pNextNode = pOldNext;
pNewNode->m_pPrevNode = pOldNode;
if (NULL == pOldNext)
{
m_pLeafTail = pNewNode;
}
else
{
pOldNext->m_pPrevNode = pNewNode;
}
// 判断是插入到原结点还是新结点中,确保是按数据值排序的
if (data < key)
{
pOldNode->Insert(data); // 插入原结点
}
else
{
pNewNode->Insert(data); // 插入新结点
}
// 父结点
CInternalNode* pFather = (CInternalNode*)(pOldNode->GetFather());
// 如果原结点是根节点,对应情况2
if (NULL == pFather)
{
CNode* pNode1 = new CInternalNode;
pNode1->SetPointer(1, pOldNode); // 指针1指向原结点
pNode1->SetElement(1, key); // 设置键
pNode1->SetPointer(2, pNewNode); // 指针2指向新结点
pOldNode->SetFather(pNode1); // 指定父结点
pNewNode->SetFather(pNode1); // 指定父结点
pNode1->SetCount(1);
SetRoot(pNode1); // 指定新的根结点
return true;
}
// 情况3和情况4在这里实现
bool ret = InsertInternalNode(pFather, key, pNewNode);
return ret;
}
/* 删除某数据
删除数据的算法如下:
(1) 如果删除后叶子结点填充度仍>=50%,只需要修改叶子结点,如果删除的是父结点的键,父结点也要相应修改;
(2) 如果删除后叶子结点填充度<50%,需要先找到一个最近的兄弟结点(左右均可),然后分两种情况:
A. 如果该兄弟结点填充度>50%,把该兄弟结点的最近一个数据剪切到本结点,父结点的键值也要相应修改。
B. 如果该兄弟结点的填充度=50%,则把两个结点合并,父结点键也相应合并。(如果合并后父结点的填充度<50%,则需要递归)
*/
bool BPlusTree::Delete(KEY_TYPE data)
{
// 查找理想的叶子结点
CLeafNode* pOldNode = SearchLeafNode(data);
// 如果没有找到,返回失败
if (NULL == pOldNode)
{
return false;
}
// 删除数据,如果失败一定是没有找到,直接返回失败
bool success = pOldNode->Delete(data);
if (false == success)
{
return false;
}
// 获取父结点
CInternalNode* pFather = (CInternalNode*)(pOldNode->GetFather());
if (NULL == pFather)
{
// 如果一个数据都没有了,删除根结点(只有根节点可能出现此情况)
if (0 == pOldNode->GetCount())
{
delete pOldNode;
m_pLeafHead = NULL;
m_pLeafTail = NULL;
SetRoot(NULL);
}
return true;
}
// 删除后叶子结点填充度仍>=50%,对应情况1
if (pOldNode->GetCount() >= ORDER_V)
{
for (int i = 1; (data >= pFather->GetElement(i)) && (i <= pFather->GetCount()); i++)
{
// 如果删除的是父结点的键值,需要更改该键
if (pFather->GetElement(i) == data)
{
pFather->SetElement(i, pOldNode->GetElement(1)); // 更改为叶子结点新的第一个元素
}
}
return true;
}
// 找到一个最近的兄弟结点(根据B+树的定义,除了叶子结点,总是能找到的)
int flag = FLAG_LEFT;
CLeafNode* pBrother = (CLeafNode*)(pOldNode->GetBrother(flag));
// 兄弟结点填充度>50%,对应情况2A
KEY_TYPE NewData = INVALID;
if (pBrother->GetCount() > ORDER_V)
{
if (FLAG_LEFT == flag) // 兄弟在左边,移最后一个数据过来
{
NewData = pBrother->GetElement(pBrother->GetCount());
}
else // 兄弟在右边,移第一个数据过来
{
NewData = pBrother->GetElement(1);
}
pOldNode->Insert(NewData);
pBrother->Delete(NewData);
// 修改父结点的键值
if (FLAG_LEFT == flag)
{
for (int i = 1; i <= pFather->GetCount() + 1; i++)
{
if (pFather->GetPointer(i) == pOldNode && i > 1)
{
pFather->SetElement(i - 1 , pOldNode->GetElement(1)); // 更改本结点对应的键
}
}
}
else
{
for (int i = 1; i <= pFather->GetCount() + 1; i++)
{
if (pFather->GetPointer(i) == pOldNode && i > 1)
{
pFather->SetElement(i - 1, pOldNode->GetElement(1)); // 更改本结点对应的键
}
if (pFather->GetPointer(i) == pBrother && i > 1)
{
pFather->SetElement(i - 1 , pBrother->GetElement(1)); // 更改兄弟结点对应的键
}
}
}
return true;
}
// 情况2B
// 父结点中要删除的键
KEY_TYPE NewKey = NULL;
// 把本结点与兄弟结点合并,无论如何合并到数据较小的结点,这样父结点就无需修改指针
if (FLAG_LEFT == flag)
{
(void)pBrother->Combine(pOldNode);
NewKey = pOldNode->GetElement(1);
CLeafNode* pOldNext = pOldNode->m_pNextNode;
pBrother->m_pNextNode = pOldNext;
// 在双向链表中删除结点
if (NULL == pOldNext)
{
m_pLeafTail = pBrother;
}
else
{
pOldNext->m_pPrevNode = pBrother;
}
// 删除本结点
delete pOldNode;
}
else
{
(void)pOldNode->Combine(pBrother);
NewKey = pBrother->GetElement(1);
CLeafNode* pOldNext = pBrother->m_pNextNode;
pOldNode->m_pNextNode = pOldNext;
// 在双向链表中删除结点
if (NULL == pOldNext)
{
m_pLeafTail = pOldNode;
}
else
{
pOldNext->m_pPrevNode = pOldNode;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -