B树是什么,不用说了,相信大家都懂,我今天只想详细地说一下B树的插入和删除的详细算法,为了以后自己看起来方便,并且附上自己的代码,希望对各位也能够有所帮助。
B树的插入算法:
1)用查找的方法为x找到所在的位置,查找路径终于某个空树,然后把x插入到其父结点的有序位置上,注意B树所有的插入都是在叶结点上进行。
2)如果插入x后,结点不超长,则插入完毕;否则,进入下一步,进行超长处理。
3)将超长结点一分为二,将“中间元素”递归地插入上层结点中。
4)如插入波及根,当根上溢时,把根一分为2,并将中间元素上移,而产生含单元素的新根,使B树升高。
将超长结点一分为二的操作方法:
设结点d超长,即d的元素达M个,令k=M/2向上取整,元素k作为中间元素,其左侧的元素和相应的指针仍保存在结点d中,但是需要修改d的长度,将排在k元素右侧的元素和相应的指针移入新结点e中,将k连同指向结点e的指针一起插入到d的父结点f中,排在指向结点d的指针右侧。
注意:
为了避免回溯,我的代码从上到下在查找x位置的时候,只要遇见的结点是满的,就先分解,这样,当我们插入完成后,即使需要超长处理,也只会停止在其父亲结点,不会波及更上层的结点。
我的插入代码如下:
bool CBTree::InsertNode(int nKey)
{
int i = 0;
if (m_Root == NULL)
{
/*
如果是空树,就生成一个新的节点,修改关键字个数,设置关键字,设为叶节点,然后,修改根指针。
*/
BNode * pNewNode = new BNode;
if (!pNewNode)
return false;
pNewNode->m_Key[0] = 1;
pNewNode->m_Key[1] = nKey;
pNewNode->m_bLeaf = true;
m_Root = pNewNode;
return true;
}
//非空,查找叶节点
BNode * pCurNode = m_Root;
BNode * pNewNode = NULL;
BNode * pParent = NULL;
int nParentIndex = 0;
BNode * pChild = NULL;
int nChildIndex = 0;
int nMidKey = 0;
int k = 0;
/*
寻找到叶节点,因为B树是在叶节点上插入,在从根节点到叶节点的路径上,如果遇见满节点,就对其进行分裂,这样的好处,就是如果
叶节点分裂,不需要回溯,直接在父亲节点上插入即可,因为,在到达叶节点之前,已经对所有的满节点进行了分裂。
*/
while (pCurNode->m_bLeaf == false)
{
/*
遍历当前节点的关键字,如果有相等的,就直接返回,因为已经存在了,否则,找到子树,并保存在pChild和nChildIndex。
*/
for (i = 1; i <= pCurNode->m_Key[0]; i++)
{
if (nKey == pCurNode->m_Key[i])
{
//已经存在,插入返回
return true;
}
if (nKey < pCurNode->m_Key[1])
{
pChild = pCurNode->m_ChildRoot[0];
nChildIndex = 0;
break;
}
if (nKey < pCurNode->m_Key[i])
{
pChild = pCurNode->m_ChildRoot[i-1];
nChildIndex = i - 1;
break;
}
}
if (i > pCurNode->m_Key[0])
{
pChild = pCurNode->m_ChildRoot[i - 1];
nChildIndex = pCurNode->m_Key[0];
}
//判断节点是否满了,如果满了则分裂。
if (pCurNode->m_Key[0] == B_TREE_ORDER - 1)
{
pNewNode = new BNode;
if (!pNewNode)
return false;
pNewNode->m_bLeaf = false;
nMidKey = (B_TREE_ORDER + 1) / 2; //向上取整加1
//拷贝关键字
for (i = nMidKey + 1; i <= pCurNode->m_Key[0]; i++)
{
pNewNode->m_Key[pNewNode->m_Key[0] + 1] = pCurNode->m_Key[i];
pNewNode->m_Key[0]++;
}
//拷贝孩子
for (i = nMidKey ; i <= pCurNode->m_Key[0] + 1; i++)
{
pNewNode->m_ChildRoot[i - nMidKey] = pCurNode->m_ChildRoot[i];
}
if (pParent == NULL)
{
//根节点
BNode * pTmp = new BNode;
if (!pTmp)
{
if (pNewNode)
delete pNewNode;
return false;
}
pTmp->m_bLeaf = false;
pTmp->m_Key[0] = 1;
pTmp->m_Key[1] = pCurNode->m_Key[nMidKey];
pTmp->m_ChildRoot[0] = pCurNode;
pTmp->m_ChildRoot[1] = pNewNode;
m_Root = pTmp;
pCurNode->m_Key[0] = nMidKey - 1;
}
else
{
//非根节点,移动关键字,移动孩子
for (i = pParent->m_Key[0]; i > nParentIndex; i--)
{
pParent->m_Key[i + 1] = pParent->m_Key[i];
}
for (i = pParent->m_Key[0] + 1; i > nParentIndex; i--)
{
pParent->m_ChildRoot[i + 1] = pParent->m_ChildRoot[i];
}
pParent->m_Key[nParentIndex + 1] = pCurNode->m_Key[nMidKey];
pParent->m_ChildRoot[nParentIndex + 1] = pNewNode;
pParent->m_Key[0]++;
pCurNode->m_Key[0] = nMidKey - 1;
}
if (nChildIndex >= nMidKey)
{
pParent = pNewNode;
nParentIndex = nChildIndex - nMidKey;
}
else
{
pParent = pCurNode;
nParentIndex = nChildIndex;
}
}
else
{
pParent = pCurNode;
nParentIndex = nChildIndex;
}
pCurNode = pChild;
}
//已经完成了子树查找
//在叶子插入数据
for (i = 1; i <= pCurNode->m_Key[0]; i++)
{
if (pCurNode->m_Key[i] == nKey)
return true;
if (nKey < pCurNode->m_Key[i])
{
break;
}
}
nChildIndex = i;
int m = 0;
if (pCurNode->m_Key[0] < B_TREE_ORDER - 1)
{
//插入完毕
for (m = pCurNode->m_Key[0]; m >= nChildIndex; m--)
{
pCurNode->m_Key[m + 1] = pCurNode->m_Key[m];
}
pCurNode->m_Key[nChildIndex] = nKey;
pCurNode->m_Key[0]++;
}
else
{
//分解
pNewNode = new BNode;
nMidKey = (B_TREE_ORDER + 1) / 2;
int nMidKeyVal = pCurNode->m_Key[nMidKey];
//因为叶节点没有孩子,所以不需要拷贝孩子。
for (m = nMidKey + 1; m <= pCurNode->m_Key[0]; m++)
{
pNewNode->m_Key[pNewNode->m_Key[0]+1] = pCurNode->m_Key[m];
pNewNode->m_Key[0]++;
}
pNewNode->m_bLeaf = true;
pCurNode->m_Key[0] = nMidKey - 1;
//非根节点,移动关键字,移动孩子
if (pParent == NULL)
{
//叶子节点就是根节点
BNode * pTmp = new BNode;
if (!pTmp)
{
if (pNewNode)
delete pNewNode;
return false;
}
pTmp->m_bLeaf = false;
pTmp->m_Key[0] = 1;
pTmp->m_Key[1] = nMidKeyVal;
pTmp->m_ChildRoot[0] = pCurNode;
pTmp->m_ChildRoot[1] = pNewNode;
m_Root = pTmp;
}
else
{
//移动关键字
for (i = pParent->m_Key[0]; i > nParentIndex; i--)
{
pParent->m_Key[i + 1] = pParent->m_Key[i];
}
//移动孩子
for (i = pParent->m_Key[0]; i > nParentIndex; i--)
{
pParent->m_ChildRoot[i + 1] = pParent->m_ChildRoot[i];
}
pParent->m_Key[nParentIndex + 1] = pCurNode->m_Key[nMidKey];
pParent->m_ChildRoot[nParentIndex + 1] = pNewNode;
pParent->m_Key[0]++;
}
if (nKey > nMidKeyVal)
{
//在新节点插入
for (i = 1; i <= pNewNode->m_Key[0]; i++)
{
if (nKey < pNewNode->m_Key[i])
{
for (k = pNewNode->m_Key[0]; k >= i; k--)
{
pNewNode->m_Key[k + 1] = pNewNode->m_Key[k];
}
pNewNode->m_Key[i] = nKey;
pNewNode->m_Key[0]++;
break;
}
}
if (i > pNewNode->m_Key[0])
{
pNewNode->m_Key[++(pNewNode->m_Key[0])] = nKey;
}
}
else
{
//在老节点插入
for (i = 1; i <= pCurNode->m_Key[0]; i++)
{
if (nKey < pCurNode->m_Key[i])
{
for (k = pCurNode->m_Key[0]; k >= i; k--)
{
pCurNode->m_Key[k + 1] = pCurNode->m_Key[k];
}
pCurNode->m_Key[i] = nKey;
pCurNode->m_Key[0]++;
break;
}
}
if (i > pCurNode->m_Key[0])
{
pCurNode->m_Key[++(pCurNode->m_Key[0])] = nKey;
}
}
}
return true;
}
在实现的过程中,发现了一个细节,就是2-3树不是B树,最小的B树是2-3-4树。
下面我们说一下,B树的删除算法,具体如下:
1)经查找,在某一结点c中找到x。
2)根据x所在的位置不同,做出不同的处理。
a)如果c是叶结点,直接删除x。
b) 如果c不是叶结点,查找ta的中序前驱,调换x和它的中序前驱,x的中序前驱一定在叶结点中。
将最终的叶结点记为d。
3)如果删除d后不下溢,删除结束;否则,进入下溢处理。
4)如果d是根,转到步骤8;否则,进入下一步。
5)找d的临近兄弟e,如果e的长度没有到达下限,那么借e的一个元素,注意相应的指针也要借过来,否则分支结点的借取就会出错。然后调整d和e之间的那个元素。
6)如果d的临近兄弟e都已经到达下限,那么合并d和e,注意合并的时候,d的父结点f中的相应关键字也会合并进来。
7)如果导致d的父结点f下溢,那么令d=f,然后转到步骤4。
8)根下溢,即根的长度变为0,删除根,整个B的高度下降。删除结束。
删除代码具体如下:
bool CBTree::RemoveNode(int nKey)
{
//查找结点
int i = 0;
int k = 0;
//下溢
int nLowNum = (B_TREE_ORDER + 1) / 2 - 1;
BNode * pTmp = m_Root;
if (!pTmp)
return false;
BNode * pFind = NULL;
int nFindIndex = 0;
stack<BNode *> sPath;
BNode * pLeft = NULL;
BNode * pRight = NULL;
while (pTmp)
{
for (i = 1; i <= pTmp->m_Key[0]; i++)
{
if (nKey == pTmp->m_Key[i])
{
pFind = pTmp;
nFindIndex = i;
break;
}
if (nKey < pTmp->m_Key[i])
{
break;
}
}
if (pFind)
break;
//将父节点入栈
sPath.push(pTmp);
if (i > pTmp->m_Key[0])
{
pTmp = pTmp->m_ChildRoot[pTmp->m_Key[0]];
}
else
{
pTmp = pTmp->m_ChildRoot[i - 1];
}
}
if (!pFind)
{
printf("not found %d\n", nKey);
return false;
}
//printf("find %d\n", nKey);
if (pFind->m_bLeaf == false)
{
//不是叶节点,调成叶节点
//查找中序前驱
BNode * pPrevRoot = NULL;
for (i = 1; i <= pFind->m_Key[0]; i++)
{
if (pFind->m_Key[i] == nKey)
{
pPrevRoot = pFind->m_ChildRoot[i - 1];
break;
}
}
if (i > pFind->m_Key[0])
return false;
while (pPrevRoot->m_bLeaf == false)
{
sPath.push(pPrevRoot);
pPrevRoot = pPrevRoot->m_ChildRoot[pPrevRoot->m_Key[0]];
}
int nTmp = pPrevRoot->m_Key[pPrevRoot->m_Key[0]];
pPrevRoot->m_Key[pPrevRoot->m_Key[0]] = nKey;
pFind->m_Key[i] = nTmp;
pFind = pPrevRoot;
}
//叶节点删除,有可能本来就是叶节点,也有可能是调成的叶节点
for (i = 1; i <= pFind->m_Key[0]; i++)
{
if (pFind->m_Key[i] == nKey)
{
for (k = i; k <= pFind->m_Key[0]; k++)
{
pFind->m_Key[i] = pFind->m_Key[i + 1];
}
pFind->m_Key[0]--;
}
}
//判断是否下溢
if (sPath.empty())
{
//根节点下溢,因为根节点也有可能是叶节点
if (pFind->m_Key[0] <= 0)
{
m_Root = NULL;
delete pFind;
}
return true;
}
else
{
//非根节点下溢
if (nLowNum > pFind->m_Key[0])
{
//下溢
while (sPath.empty() == false)
{
BNode * pParent = sPath.top();
sPath.pop();
if (pParent)
{
for (i = 0; i <= pParent->m_Key[0]; i++)
{
if (pParent->m_ChildRoot[i] == pFind)
{
if (i == 0)
{
pLeft = NULL;
pRight = pParent->m_ChildRoot[1];
}
else if (i < pParent->m_Key[0])
{
pLeft = pParent->m_ChildRoot[i - 1];
pRight = pParent->m_ChildRoot[i + 1];
}
else
{
pLeft = pParent->m_ChildRoot[i - 1];
pRight = NULL;
}
break;
}
}
if (i > pParent->m_Key[0])
{
printf("error:458");
return false;
}
//左借
if (pLeft && (pLeft->m_Key[0] > nLowNum))
{
int nTmp = pParent->m_Key[i];
pParent->m_Key[i] = pLeft->m_Key[pLeft->m_Key[0]];
for (k = pFind->m_Key[0]; k >= 1; k--)
{
pFind->m_Key[k + 1] = pFind->m_Key[k];
}
for (k = pFind->m_Key[0]; k >= 0; k--)
{
pFind->m_ChildRoot[k + 1] = pFind->m_ChildRoot[k];
}
pFind->m_Key[1] = nTmp;
pFind->m_ChildRoot[0] = pLeft->m_ChildRoot[pLeft->m_Key[0]];
pFind->m_Key[0]++;
pLeft->m_Key[0]--;
return true;
}
//右借
if (pRight && (pRight->m_Key[0] > nLowNum))
{
int nTmp = pParent->m_Key[i+1];
BNode * pRightFirstChilid = pRight->m_ChildRoot[0];
pParent->m_Key[i+1] = pRight->m_Key[1];
for (k = 2; k <= pRight->m_Key[0]; k++)
{
pRight->m_Key[k-1] = pRight->m_Key[k];
}
for (k = 1; k <= pRight->m_Key[0]; k++)
{
pRight->m_ChildRoot[k - 1] = pRight->m_ChildRoot[k];
}
pRight->m_Key[0]--;
pFind->m_Key[pFind->m_Key[0] + 1] = nTmp;
pFind->m_Key[0]++;
pFind->m_ChildRoot[pFind->m_Key[0]] = pRightFirstChilid;
return true;
}
//左合并
if (pLeft && pLeft->m_Key[0] >= 1)
{
pLeft->m_Key[pLeft->m_Key[0] + 1] = pParent->m_Key[i];
pLeft->m_Key[0]++;
for (k = 0; k <= pFind->m_Key[0]; k++)
{
pLeft->m_ChildRoot[pLeft->m_Key[0] + k] = pFind->m_ChildRoot[k];
}
for (k = 1; k <= pFind->m_Key[0]; k++)
{
pLeft->m_Key[pLeft->m_Key[0] + 1] = pFind->m_Key[k];
pLeft->m_Key[0]++;
}
for (k = i; k < pParent->m_Key[0]; k++)
{
pParent->m_Key[k] = pParent->m_Key[k + 1];
pParent->m_ChildRoot[k] = pParent->m_ChildRoot[k + 1];
}
pParent->m_Key[0]--;
delete pFind;
if (pParent->m_Key[0] < nLowNum)
{
pFind = pParent;
continue;
}
return true;
}
//右合并
if (pRight && pRight->m_Key[0] >= 1)
{
pFind->m_Key[pFind->m_Key[0] + 1] = pParent->m_Key[i + 1];
pFind->m_Key[0]++;
for (k = 0; k <= pRight->m_Key[0]; k++)
{
pFind->m_ChildRoot[pFind->m_Key[0] + k] = pRight->m_ChildRoot[k];
}
for (k = 1; k <= pRight->m_Key[0]; k++)
{
pFind->m_Key[pFind->m_Key[0] + 1] = pRight->m_Key[k];
pFind->m_Key[0]++;
}
for (k = i + 1; k < pParent->m_Key[0]; k++)
{
pParent->m_Key[k] = pParent->m_Key[k + 1];
pParent->m_ChildRoot[k] = pParent->m_ChildRoot[k + 1];
}
pParent->m_Key[0]--;
delete pRight;
if (pParent->m_Key[0] < nLowNum)
{
pFind = pParent;
continue;
}
return true;
}
printf("error:503\n");
return false;
}
printf("error:596\n");
return false;
}
//根节点下溢只要不是0就可以
if (pFind->m_Key[0] == 0)
{
m_Root = pFind->m_ChildRoot[0];
delete pFind;
}
return true;
}
return true;
}
}
具体运行效果如下:
打印1-10的B树
需要代码可以从下面下载:
https://download.csdn.net/download/u011711997/10414495