学习笔记:B树建立,搜索和删除操作

#include <iostream>
#include <queue>
using namespace std;

/******************************
PROGRAMER: Fanchenxin
M阶多路查找树:
1.定义任意非叶子结点最多只有M个儿子;且M>2;

       2.根结点的儿子数为[2, M];

       3.除根结点以外的非叶子结点的儿子数为[M/2, M];

       4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)

       5.非叶子结点的关键字个数=指向儿子的指针个数-1;

       6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

       7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的

子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

       8.所有叶子结点位于同一层;


       1、每个结点的属性:   
       (1)BTreeNode.keyNum: 每个结点包含的关键字个数;  
       (2)BTreeNode.key1, BTreeNode.key2, .....BTreeNode.keyn, 以非降序排列
       (3)BTreeNode.isLeaf, 表示BTreeNode 结点是否是叶子结点。   
       (4)BTreeNode.subTree: 每个结点还包含BTreeNode.keyNum+1个孩子指针
                    BTreeNode.subTree1, BTreeNode.subTree2, BTreeNode.subTree3......BTreeNode.subTree(keyNum+1)
         (5)      BTreeNode.father 指向该节点的父亲节点。
       2、BTreeNode中的关键字对存储在各子树中的关键字范围加以分割。 
       3、每个叶结点具有相同的深度,为树的高度  
       4、我们可以为每棵B树规定一个最小度数:T,
       那么每个结点所包含的关键字个数的范围:
       T-1 ~2T-1, 所包含的孩子结点个数为T ~ 2T。
       当结点关键字个数为2T - 1时,该结点满。
       (根结点至少包含1个关键数,而其他结点至少包含T - 1个关键字。

******************************/

#define T (2)

typedef struct _B_TreeNode_
{
    int keyNum;
    char key[2*T-1];
    bool isLeaf;
    struct _B_TreeNode_* subTree[2*T];
    struct _B_TreeNode_* father;
}BTreeNode;

BTreeNode* newBTreeNode(bool is_Leaf)
{
    BTreeNode* newNode = (BTreeNode*)malloc(sizeof(BTreeNode));
    if(newNode != NULL)
    {
        newNode->keyNum = 0;
        memset(newNode->key, '\0', (2*T-1) * sizeof(char));
        newNode->isLeaf = is_Leaf;
        memset(newNode->subTree, NULL, 2*T * sizeof(BTreeNode*));
        newNode->father = NULL;
    }
    else
        return NULL;
    return newNode;
}

void deleteKey(BTreeNode* pCur, const char keyChar);


/* father为需要分裂的节点的父节点
    splitChildIdx 为需要分裂的节点的index, 
*/
void splitChild(BTreeNode* father, int splitChildIdx)
{
    if(!father)
        return;
    /*father->subTree[splitChildIdx] 为需要分裂的节点,分裂完后作为
        父节点subTree相应位置的左孩子*/
    BTreeNode* leftChild = father->subTree[splitChildIdx];
    BTreeNode* rightChild = newBTreeNode(true); /* 新分裂出来的节点放在分裂点的右边 */
    rightChild->father = father;
    int i = 0, n = T-1;
    char midChar = leftChild->key[n]; /* 保存中间KEY 值 */
    for(; i < n; i++)
    {
        rightChild->key[i] = leftChild->key[i+T]; /* 将分裂节点的后T-1个KEY给右孩子 */
    }
    memset(&leftChild->key[n], '\0', sizeof(char)*T); /* 清空分裂节点后面的T个KEY 值 */

    leftChild->keyNum = n; /* 分裂成的两个节点key 的个数都为T-1 */
    rightChild->keyNum = n;

    /* 如果该分裂节点不是叶子节点,也就是说它存在子节点
        则需要将后面T个子节点送给右节点*/
    if(leftChild->isLeaf == false){
        rightChild->isLeaf = false;
        for(i = 0; i < n + 1; i++){
            rightChild->subTree[i] = leftChild->subTree[i+n+1];
            rightChild->subTree[i]->father = rightChild;
        }
        /* 同时清除分裂节点的后面T个子节点指针 */
	 memset(&leftChild->subTree[n+1], NULL, sizeof(BTreeNode*) * T);
    }

    for(i = father->keyNum; i >= splitChildIdx; i--){
        father->subTree[i+1] = father->subTree[i];
    }
    father->keyNum++;
    father->subTree[splitChildIdx+1] = rightChild; /*将新分裂出来的右节点加到父节点的相应位置*/

    for(i = father->keyNum-1; i >= splitChildIdx; i--){
        father->key[i+1] = father->key[i];
    }
    father->key[splitChildIdx] = midChar; /* 将分裂节点的中间KEY 放到父节点的KEY数组中 */
}

/*往节点KEY个数未满的节点处插入key值*/
void insertKeyNotFull(BTreeNode* pNode, char keyChar)
{
    if(!pNode)
        return;
    
    if(pNode->keyNum == 0){
        pNode->key[0] = keyChar;
	 pNode->keyNum = 1;
        return;
    }

    int idx = pNode->keyNum - 1;
    if(pNode->isLeaf){ /* 如果该节点是叶子节点则找到合适位置并插入 */
        for(; idx >= 0; idx--)
        {
            if(keyChar < pNode->key[idx])
                pNode->key[idx+1] = pNode->key[idx];
            else
                break;
        }

        pNode->key[idx+1] = keyChar;
	 pNode->keyNum++;
    }
    else{
        for(; idx >= 0; idx--)
        {
            if(keyChar > pNode->key[idx])
                break;
        }
        idx += 1;

        /* 如果该节点的子孩子需要分裂 */
        if(pNode->subTree[idx]->keyNum >= 2*T -1){
            splitChild(pNode, idx);
            if(keyChar > pNode->key[idx])
                idx += 1;
        }
        insertKeyNotFull(pNode->subTree[idx], keyChar);
    }
    return;
}

void insertKey(BTreeNode** root, char keyChar)
{
    if(*root == NULL){
        *root = newBTreeNode(true);
    }

    BTreeNode* pCur = *root;
    if(pCur->keyNum >= 2*T-1){
        BTreeNode* newRoot = newBTreeNode(false);
        newRoot->subTree[0] = *root;
        newRoot->subTree[0]->father = newRoot;
        *root = newRoot;

        splitChild(newRoot, 0);
        insertKeyNotFull(newRoot, keyChar);
    }
    else{
        insertKeyNotFull(*root, keyChar);
    }

    return;
}

void printKey(char* arry, int n, int layer)
{
    int i = 0;
	cout << "<" << layer << "> ";
    for(; i < n; i++)
        cout << arry[i];
    cout << "   ";
}

static int layer = 1;

/* 深度优先遍历: layer 为该节点所在的层*/
void DPSearch(BTreeNode* pNode)
{
    if(pNode == NULL)
        return;
    printKey(pNode->key, pNode->keyNum, layer);
    int i = 0; 
    layer++;
    for(; i < pNode->keyNum+1; i++){
		DPSearch(pNode->subTree[i]);
    }
    layer--;
    return;
}

/* 广度优先遍历 */
void BPSearch(BTreeNode* pNode)
{
	if(pNode == NULL)
		return;
	
	queue<BTreeNode*> q;
	q.push(pNode);
	BTreeNode* pCur = NULL;
	while(!q.empty())
	{
		pCur = q.front();
		printKey(pCur->key, pCur->keyNum, 0);
		q.pop();
		int i = 0;
		for(; i < pCur->keyNum+1; i++){
			if(pCur->subTree[i]){
				q.push(pCur->subTree[i]);
			}
		}
	}
}

BTreeNode* searchKey(BTreeNode* pCur, char keyChar, int* keyIndex)
{
    if(pCur== NULL)
        return NULL;

    int i = 0;
    while(i < pCur->keyNum && keyChar > pCur->key[i])
        ++i;
    if(i < pCur->keyNum && keyChar == pCur->key[i]){
        if(keyIndex)
            *keyIndex = i;
        return pCur;
    }
    
    if(pCur->isLeaf == false){
        pCur = pCur->subTree[i];
        return searchKey(pCur ,keyChar, keyIndex);
    }
    else
        return NULL;
}

BTreeNode* create_Btree(const char* key, int n)
{
    BTreeNode* root = newBTreeNode(true);
    if(root){
        int i = 0;
        for(; i < n; i++){
            insertKey(&root, key[i]);
        }
    }
    return root;
}

/* pNode  需要删除的key 在该节点里
    isLeft标识找到的兄弟是左兄弟还是右兄弟 
    index 为pNode 在父节点的subTree数组中的位置
    返回值为true 表示找到否则没找到
    */
bool checkBrotherIsRich(BTreeNode* pNode, int* index, bool* isLeft) 
{
    if(pNode == NULL)
        return false;
    if(pNode->father == NULL)
        return false;
    BTreeNode* father = pNode->father;

    /* 查找该节点在父节点的subTree 的索引 */
    int i = 0;
    for(; i < father->keyNum+1; i++){
        if(father->subTree[i] == pNode){
            *index = i;
            break;
        }
    }

    if(i == 0) //如果该节点是最左边的孩子,则只存在右兄弟
    {
        if(father->subTree[i+1]->keyNum > T-1){
            *isLeft = false;
            return true;
        }
        else
            return false;
    }

    if(i == father->keyNum) //如果该节点是最右边的孩子,则只存在左兄弟
    {
        if(father->subTree[i-1]->keyNum > T-1){
            *isLeft = true;
            return true;
        }
        else
            return false;
    }

    if(i > 0 && i < father->keyNum){
        if(father->subTree[i-1]->keyNum > T-1){
            *isLeft = true;
            return true;
        }
        else{
            if(father->subTree[i+1]->keyNum > T-1){
                *isLeft = false;
                return true;
            }
            else
                return false;
        }
    }

    return false;
}

/* 将arry 数组index 后面的内容前移一个单位到index 位置 */
//template<typename T>
void moveProcess(char arry[], int index, int n)
{
    int i = index, j = index + 1;
    for(; j < n; j++){
        arry[i++] = arry[j];
    }
    arry[i] = '\0';
}


/* 需要旋转的是叶子节点 */
void leafRotateAndDelete(BTreeNode* pNode, int index, bool isLeft, char deleteKeyChar)
{
    if(!pNode)
        return;
    char fatherChar = '\0', brotherChar = '\0';
    BTreeNode* father = pNode->father;
    int fatherKeyNum = father->keyNum;
    BTreeNode* pBrother = NULL;
    if(isLeft == true){ /* 如果富有的兄弟节点位于删除节点的左边 */
        pBrother = father->subTree[index-1];
        fatherChar = father->key[index-1];
        brotherChar = pBrother->key[pBrother->keyNum-1];
        /* 将从父节点借的key 插入到该节点中,并删除deleteChar */
        int i = 0, j = 1;
        for(; i < pNode->keyNum; i++){
            if(pNode->key[i] != deleteKeyChar){
                pNode->key[j++] = pNode->key[i];
            }
        }
        pNode->key[j] = '\0';
        pNode->key[0] = fatherChar;

        /* 用左兄弟的最大的key 替换父亲被借走的key */
        father->key[index-1] = brotherChar;

        /* 左兄弟节点最大的key 删掉 */
        pBrother->key[pBrother->keyNum-1] = '\0';
        pBrother->keyNum--;
    }
    else{
        pBrother = father->subTree[index+1];
        fatherChar = father->key[index];
        brotherChar = pBrother->key[0];

        int i = 0, j = 0;
        for(; i < pNode->keyNum; i++){
            if(pNode->key[i] != deleteKeyChar){
                pNode->key[j++] = pNode->key[i];
            }
        }
        pNode->key[j] = fatherChar;  //从父节点借一个key 插入到删除节点的最右边

        /*从删除节点的右边兄弟节点借得最小的key替换父节点被借走的key*/
        father->key[index] = brotherChar;  

        /* 右边的兄弟节点的所有key 往前移一位 */
        moveProcess(pBrother->key, 0, pBrother->keyNum);
        pBrother->keyNum--;
    }
}


/* 将pSubRoot 为根节点的子树的所有key 重新插入pTree中 */
void insertTreeNode(BTreeNode*pTree, BTreeNode* pSubRoot)
{
    if(!pTree || !pSubRoot)
        return;

    queue<BTreeNode*> q;
    q.push(pSubRoot);
    BTreeNode* pCur = NULL;
    int i = 0;
    while(!q.empty())
    {
    	pCur = q.front();
    	for(i = 0; i < pCur->keyNum; i++){
            insertKey(&pTree, pCur->key[i]);
    	}
    	q.pop();
       
    	for(i = 0; i < pCur->keyNum+1; i++){
    		if(pCur->subTree[i]){
    			q.push(pCur->subTree[i]);
    		}
    	}

       free(pCur);
       pCur = NULL;
    }

    return;
}

/* 执行合并或旋转操作 */
void mergeOrRotate(BTreeNode* pNode)
{
    if(!pNode)
        return;

    /* pNode 为根节点 */
    if(pNode->father == NULL){  //根节点,合并成一个节点 少一层
        /* 如果根节点只有一个key,且它的左右孩子的key
            都不大于T-1,那么将它们合并后总的key 的个数
            不超过(1 + T-1 + T-1) = 2*T -1, 因此可以将三个节点
            合并成一个节点 */
        if(pNode->keyNum == 1 &&  
                pNode->subTree[0]->keyNum <= T-1
                && pNode->subTree[1]->keyNum <= T-1){
                BTreeNode* left = pNode->subTree[0];
                BTreeNode* right = pNode->subTree[1];

                char tmp = pNode->key[0];
                int i = 0, j = 0;
                for(; j < left->keyNum; j++){
                    pNode->key[j] = left->key[j];
                }
                pNode->key[j] = tmp;
                pNode->keyNum += left->keyNum;

                for(i = 0; i < left->keyNum+1; i++){
                    pNode->subTree[i] = left->subTree[i];
                    pNode->subTree[i]->father = pNode;
                }

                for(i = pNode->keyNum, j = 0; j < right->keyNum; j++){
                    pNode->key[i] = right->key[j];
                    pNode->subTree[i] = right->subTree[j];
                    pNode->subTree[i]->father = pNode;
                    i++;
                }
                pNode->subTree[i] = right->subTree[j];
                pNode->subTree[i]->father = pNode;

                pNode->keyNum += right->keyNum;

                free(left);
                free(right);
                left = NULL;
                right = NULL;
        }
        return;
    }

    /* 如果不是根节点 */
    bool isLeft = true; int sub_idx = 0;
    /* sub_idx 为pNode 在父节点subTree的位置,
        isLeft 标记找到的富裕的节点是左还是右兄弟*/
    bool isFind = checkBrotherIsRich(pNode, &sub_idx, &isLeft);
    if(isFind){ // 如果能找到比较富裕的兄弟节点,借个节点进行旋转操作
        if(isLeft == false){ //如果右兄弟富裕则左旋操作
            BTreeNode* pRight = pNode->father->subTree[sub_idx+1];
            char tmp = pNode->father->key[sub_idx];
            pNode->father->key[sub_idx] = pRight->key[0]; //用右节点的第一个key覆盖父节点的key
            /* 右孩子的key 前移 */
            moveProcess(pRight->key, 0, pRight->keyNum);
            
            BTreeNode* pRightFirstChild = pRight->subTree[0]; //保存右孩子的第一子树的节点指针
            /* 右孩子子树指针前移 */
            int i = 0, j = 1;
            for(; j < pRight->keyNum+1; j++){
                pRight->subTree[i++] = pRight->subTree[j];
            }
            pRight->subTree[i] = NULL;
            pRight->keyNum--;

            /* 将右子树的第一个子树挂接到pNode的最大子树处
                并且将tmp 这个key 放在最大处*/
            int n = pNode->keyNum;
            pNode->key[n] = tmp;
            n = pNode->keyNum++;
            pNode->subTree[n+1] = pRightFirstChild;
            pRightFirstChild->father = pNode;
        }else{ // 如果左兄弟富裕则进行右旋操作 
            BTreeNode* pLeft = pNode->father->subTree[sub_idx-1];
            char tmp = pNode->father->key[sub_idx];
            int n = pLeft->keyNum;
            pNode->father->key[sub_idx] = pLeft->key[n-1]; //用左节点的最大一个key覆盖父节点的key

            pLeft->key[n-1] = '\0';
            
            BTreeNode* pLeftLastChild = pLeft->subTree[n]; 
            pLeft->subTree[n] = NULL;
            pLeft->keyNum--;

            int i = 0, j = 1;
            for(; i < pNode->keyNum; i++){
                pNode->key[j++] = pNode->key[i];
            }
            pNode->key[0] = tmp;
            for(i = 0, j = 1; i < pNode->keyNum+1; i++){
                pNode->subTree[j++] = pNode->subTree[i];
            }
            pNode->subTree[0] = pLeftLastChild;
            pLeftLastChild->father = pNode;
            pNode->keyNum++;
        }
    }else{// 如果左右兄弟节点都不富裕
        if(pNode->keyNum == 0){// 如果当前节点的key被删空了
            BTreeNode* pCur = pNode;
            BTreeNode* pFather = pCur->father;
            if(pFather->keyNum > T -1){ //如果它的父节点还富裕
                int idx = 0;
                for(; idx < pFather->keyNum+1; idx++){
                    if(pFather->subTree[idx] == pCur)
                        break;
                }

                BTreeNode* pBrother = NULL;
                if(idx == pFather->keyNum){
                    pBrother = pFather->subTree[idx-1];
                    int n = pBrother->keyNum;
                    pBrother->key[n] = pFather->key[idx-1];
                    moveProcess(pFather->key, idx-1, pFather->keyNum);
                    pBrother->subTree[n+1] = pCur->subTree[0];
                    pBrother->subTree[n+1]->father = pBrother;
                    pFather->subTree[idx] = NULL;

                    pBrother->keyNum++;
                    pFather->keyNum--;

                    free(pCur);
                    pCur = NULL;
                }
                else{
                    pBrother = pFather->subTree[idx+1];
                    pCur->key[0] = pFather->key[idx];
                    pCur->keyNum = 1;
                    moveProcess(pFather->key, idx, pFather->keyNum);
                    int i = 1, j = 0;
                    for(; j < pBrother->keyNum; j++){
                        pCur->key[i++]  = pBrother->key[j];
                    }

                    for(i = 1, j = 0; j < pBrother->keyNum+1; j++){
                        pCur->subTree[i] = pBrother->subTree[j];
                        pCur->subTree[i]->father = pCur;
                        i++;
                    }
                    pCur->keyNum += pBrother->keyNum;
                    pFather->keyNum--;

                    free(pBrother);
                    pBrother = NULL;
                }
            }
            else{ //如果父节点也不富裕则递归调用
                mergeOrRotate(pNode->father);
            }
            return;
        }
        else{ //如果该节点既不是父节点,左右兄弟也不富裕,并且key个数也不为0
            mergeOrRotate(pNode->father);
        }
    }
}


/* pNode 是叶子节点,index 为其在父节点subTree 中的索引 */
void leafMergeAndDelete(BTreeNode* pNode, int index, const char deleteKeyChar)
{
    if(pNode == NULL)
        return;
    char fatherChar = '\0';
    BTreeNode* father = pNode->father;
    BTreeNode* brother = NULL;
    
    if(index == 0){ /* 如果删除节点位于最左边只能和右边的节点进行merge */
        fatherChar = father->key[0];
        brother = father->subTree[1];
        /* 虽然删除了一个key 但是父节点的key添加在其最右边以此
            key  的个数没变*/
        int step = pNode->keyNum; 
        /* 将右边的兄弟节点的[0~step) 位置留给pNode和父节点的key */
        int i = brother->keyNum + step - 1, j = brother->keyNum - 1;
        for(; i >= step; i--){
            brother->key[i] = brother->key[j--];
        }
        /* 将删除节点pNode 除了deleteChar 复制到brother节点 */
        for(i = 0, j = 0; i < step; i++)
        {
            if(pNode->key[i] != deleteKeyChar)
                brother->key[j++] = pNode->key[i];
        }
        brother->key[step-1] = fatherChar;
        brother->keyNum += step;
        
        free(pNode);  //删除该节点
        pNode = NULL;

        /* 将父节点的key 索引前移 */
        moveProcess(father->key, 0, father->keyNum);

        /*父节点的子树索引前移*/
        for(i = 1, j = 0; i < father->keyNum+1; i++){
            father->subTree[j++] = father->subTree[i];
        }
        father->subTree[j] = NULL;
        father->keyNum--;
    }
    else{
        fatherChar = father->key[index-1];
        brother = father->subTree[index-1];
        moveProcess(father->key, index-1, father->keyNum);
        int n = brother->keyNum;
        brother->key[n] = fatherChar;
        int i = n+1, j = 0;
        for(; j < pNode->keyNum; j++){
            if(pNode->key[j] != deleteKeyChar){
                brother->key[i++] = pNode->key[j];
            }
        }

        for(i = index, j = index+1; j < father->keyNum+1; j++){
            father->subTree[i++] = father->subTree[j];
        }
        father->subTree[i] = NULL;

        father->keyNum--;
        brother->keyNum += pNode->keyNum;

        free(pNode);  //删除该节点
        pNode = NULL;
    }

    /* case 3.2 : merger 后父节点变得不富裕 */
    if(father->keyNum < T-1){ 
       
            /* 
                       空                       
                        |          ==>     UV
                       UV                 
            */

        if(father->keyNum == 0 && father->father == NULL){
            BTreeNode* ptmp = father->subTree[0];
            int i = 0;
            for(; i < ptmp->keyNum; i++){
                father->key[i] = ptmp->key[i];
            }
            father->keyNum = i;
            father->isLeaf = true;
            father->subTree[0] = NULL;
            free(ptmp);
            ptmp = NULL;
            return;
        }

        mergeOrRotate(father);
       
    }

    // case 3.1 如果合并后父节点任然富裕,则删除结束返回
    return;
}


/***************************************************************************
(1) 找不到包含keyChar 的节点,直接return
(2) 以下的case 都是能够找到删除节点的情况
    <1>删除的节点是叶子节点:
    case 1: 删除一个KEY 后,key 的个数不小于T-1,则直接删除
    case 2: 删除后key的个数小于T-1,这时候如果其
                相邻的兄弟节点(富有的)的个数有大于T-1的,借
                一个KEY,并且将富有的兄弟节点的最邻近父节点的
                KEY移到父节点上,父节点的KEY 则移到删除节点。
    case 3: 如果左右兄弟节点都不富有,也就是说他们的key个
                数都是T-1的情况。
            case 3.1: 如果父节点富裕,则将删除节点和相邻的一个
                          兄弟节点合并再删除。
            case 3.2: 如果父节点也不富裕,进行合并旋转操作等
                         操作并删除KEY。
    <2>删除的节点不是叶子节点:
          先找到这个key所在的右子树的最小key , 用这个最小key
          来替换要删除的key,然后再从叶子节点删除最小key。
          把删除的节点不是叶子节点转换为在叶子节点上删除
          key 的情况。
    
*********************************************************************************/
void deleteKey(BTreeNode* pCur, const char deleteKeyChar)
{
    if(!pCur)
        return;

    int key_idx = 0;
    /* 查找需要删除的key 在哪个节点上 */
    BTreeNode* pDeleteNode = searchKey(pCur, deleteKeyChar, &key_idx);
    if(!pDeleteNode)
        return;
    if(pDeleteNode->isLeaf == true){  //  <1>删除的节点是叶子节点
        if(pDeleteNode->keyNum > T - 1){  // case 1 该节点删除一个key后也足够富裕
             /*将deleteKey 从其所在的节点删除 */
            moveProcess(pDeleteNode->key, key_idx, pDeleteNode->keyNum);
            pDeleteNode->keyNum--;
        }
        else{  //删除后不富裕
            if(pDeleteNode->father == NULL){
                if(pDeleteNode->keyNum == 1){
                    cout << "根节点只有一个key 不能再删除了!!!" << endl;
                    return;  //如果只有一个根节点,且只有一个key
                }
                else{
                    moveProcess(pDeleteNode->key, key_idx, pDeleteNode->keyNum);
                    pDeleteNode->keyNum--;
                }
                return;
            }
            
            bool isLeft = true; int sub_idx = 0;
            /* sub_idx 为pDeleteNode 在父节点subTree的位置,
                isLeft 标记找到的富裕的节点是左还是右兄弟*/
            bool isFind = checkBrotherIsRich(pDeleteNode, &sub_idx, &isLeft);
            if(isFind){ // case 2 找到富裕的兄弟节点
                leafRotateAndDelete(pDeleteNode, sub_idx, isLeft, deleteKeyChar);
            }
            else{  // case 3 左右兄弟节点都不富裕, 都只有T-1个key
                leafMergeAndDelete(pDeleteNode, sub_idx, deleteKeyChar);
            }
        }
    }
    else{  //  <2>删除的节点不是叶子节点    
        /* deleteKey 右边的子树 */
        BTreeNode* deleteRightSubTree = pDeleteNode->subTree[key_idx+1];
        BTreeNode* pCur = deleteRightSubTree;
        /* 一直找到右边子树的叶子节点的最小key */
        while(pCur->subTree[0])     
            pCur = pCur->subTree[0];

        char sm_key = pCur->key[0];
        /* 用最小key 替换要删除的key */
        pDeleteNode->key[key_idx] = sm_key;
        deleteKey(pCur, sm_key);  //从叶子节点删除最小key
    }
    return;
}

int main()
{
    BTreeNode* pRoot;
    char key[] = {'C', 'N', 'G', 'A', 'H', 'E', 'K', 'Q', 'M', 'F', 'W', 'L', 'T', 'Z', 'D', 'P', 'R', 'X', 'Y', 'S'};
    pRoot = create_Btree(key, 20);

    /*
        树建完后是这样的:
                                          G      N
                                      /      |         \
                                    C        K       Q  T  X
                                   / \      / \     / |  |  \ 
                                 A    DEF  H  LM   P  RS W  YZ
    */

    layer = 1;
    DPSearch(pRoot);
    cout << endl;

    //BPSearch(pRoot);
    //cout << endl;

    BTreeNode* pFind = NULL;
    char fChar = 'F';
    pFind = searchKey(pRoot, fChar, NULL);
    if(pFind != NULL){
        cout << "find the key: " << pFind->key << endl;
    }else{
        cout << "can not find the Key: " << fChar << endl;
    }

#if 1
    /* (1) 测试删除的key 在叶子节点的情况 */
    cout << "删除L后:" << endl;
    deleteKey(pRoot, 'L');
    DPSearch(pRoot);
    cout << endl;
    /* 删除L 后: (直接删除L)
                                          G      N
                                      /      |         \
                                    C        K       Q  T  X
                                   / \      / \     / |  |  \ 
                                 A   DEF   H   M   P  RS W  YZ
    */
	
    cout << "删除W后:" << endl;
    deleteKey(pRoot, 'W');
    DPSearch(pRoot);
    cout << endl;
    /* 删除W 后: (向父节点借T, 将左兄弟的最大key 'S' 移到父节点)
                                          G      N
                                      /      |         \
                                    C        K       Q  S  X
                                   / \      / \     / |  |  \ 
                                 A   DEF   H   M   P  R  T  YZ
    */

    cout << "删除R后:" << endl;
    deleteKey(pRoot, 'R');
    DPSearch(pRoot);
    cout << endl;
    /* 删除R 后: (左右兄弟都不富裕,将R所在的分支和左分支合并 )
                                          G      N
                                      /      |         \
                                    C        K       S  X
                                   / \      / \     /  |  \ 
                                 A   DEF   H   M   PQ  T  YZ
    */

    cout << "删除M后:" << endl;
    deleteKey(pRoot, 'M');
    DPSearch(pRoot);
    cout << endl;
    /* 删除M 后: (其实执行了一次合并和一次左旋操作。M的左右兄弟都不富裕,并且删除合并后其父节点
                                的keyNum = 0, 父节点的右兄弟SX 富裕,于是向它借一个
                                S,进行左旋操作。)
                               (1) 合并操作
                                          G      N
                                      /      |         \
                                    C        空       S  X
                                   / \       |       /  |  \ 
                                 A   DEF    HK      PQ  T  YZ
                                 
                               (2) 左旋操作: (S放到父节点,N给空节点,S的左分支
                                    挂接到N的最右孩子处。
                                          G      S
                                      /      |         \
                                    C        N         X
                                   / \      / \       /  \ 
                                 A   DEF  HK  PQ     T   YZ
    */
    
    cout << "删除H后:" << endl;
    deleteKey(pRoot, 'H');
    DPSearch(pRoot);
    cout << endl;
    /* 删除H 后: (直接删除)
                                          G      S
                                      /      |         \
                                    C        N         X
                                   / \      / \       /  \ 
                                 A   DEF   K   PQ    T   YZ
    */

    cout << "删除K后:" << endl;
    deleteKey(pRoot, 'K');
    DPSearch(pRoot);
    cout << endl;
    /* 删除K 后: (K的右兄弟富裕,借一个P 进行左旋操作)
                                          G      S
                                      /      |        \
                                    C        P         X
                                   / \      / \       /  \ 
                                 A   DEF   N   Q     T   YZ
    */

    cout << "删除N后:" << endl;
    deleteKey(pRoot, 'N');
    DPSearch(pRoot);
    cout << endl;
    /* 删除N 后: (其实进行了两次合并,N的右兄弟不富裕,
                                且父亲也只有一个节点,父亲的左右兄
                                弟也不富裕,这时候将父节点
                                和父亲的右孩合并)
                           (1)第一次合并: P和Q合并父节点变为空。
                                          G      S
                                      /      |        \
                                    C        空        X
                                   / \       |       /  \ 
                                 A   DEF    PQ      T    YZ

                           (2)第二次合并: 空的父节点和其右节点合并
                                                       G    
                                                  /           \
                                                C           S  X
                                               / \         /  |  \ 
                                             A   DEF      PQ  T  YZ
    */

    cout << "删除P后:" << endl;
    deleteKey(pRoot, 'P');
    DPSearch(pRoot);
    cout << endl;
     /* 删除P 后: 
                                          G
                                      /         \
                                    C          S  X
                                   / \       /   |  \ 
                                 A   DEF    Q    T   YZ
    */

    cout << "删除Q后:" << endl;
    deleteKey(pRoot, 'Q');
    DPSearch(pRoot);
    cout << endl;
    
    /* 删除Q 后: 合并操作
                                              G
                                          /         \
                                        C           X
                                       / \        /   \ 
                                     A   DEF     ST   YZ
        */

    cout << "删除T后:" << endl;
    deleteKey(pRoot, 'T');
    DPSearch(pRoot);
    cout << endl;
    /* 删除T 后: 
                                              G
                                          /         \
                                        C           X
                                       / \        /   \ 
                                     A   DEF      S   YZ
        */

    cout << "删除A后:" << endl;
    deleteKey(pRoot, 'A');
    DPSearch(pRoot);
    cout << endl;
    /* 删除A 后: 
                                              G
                                          /         \
                                        D           X
                                       / \        /   \ 
                                     C    EF     S     YZ
        */

    cout << "删除E后:" << endl;
    deleteKey(pRoot, 'E');
    DPSearch(pRoot);
    cout << endl;
    /* 删除E 后: 
                                              G
                                          /         \
                                        D           X
                                       / \        /   \ 
                                     C    F      S     YZ
        */

    cout << "删除F后:" << endl;
    deleteKey(pRoot, 'F');
    DPSearch(pRoot);
    cout << endl;
    /* 删除F 后: 两次合并操作
                (1) 第一次合并
                                              G
                                          /         \
                                        空           X
                                         |         /   \ 
                                        CD        S     YZ

                 (2) 第二次合并: 少了一层
                                     
                                            G    X
                                          /    |     \
                                        CD     S     YZ
        */

    cout << "删除S后:" << endl;
    deleteKey(pRoot, 'S');
    DPSearch(pRoot);
    cout << endl;
     /* 删除S 后: S 的左右节点都富裕,向左兄弟借D                     
                                            D    X
                                          /    |     \
                                         C    G     YZ
        */
    cout << "删除C后:" << endl;
    deleteKey(pRoot, 'C');
    DPSearch(pRoot);
    cout << endl;
    /* 删除C 后: C 的左右节点都不富裕,合并                    
                                             X
                                          /      \
                                         DG      YZ
        */

    cout << "删除D后:" << endl;
    deleteKey(pRoot, 'D');
    DPSearch(pRoot);
    cout << endl;
    /* 删除D 后:             
                                             X
                                          /      \
                                         G       YZ
        */

    cout << "删除G后:" << endl;
    deleteKey(pRoot, 'G');
    DPSearch(pRoot);
    cout << endl;
    /* 删除G 后:                  
                                             Y
                                          /      \
                                         X        Z
        */
        
    cout << "删除X后:" << endl;
    deleteKey(pRoot, 'X');
    DPSearch(pRoot);
    cout << endl;

        /* 删除X 后:                  
                                           YZ
        */
    cout << "删除Y后:" << endl;
    deleteKey(pRoot, 'Y');
    DPSearch(pRoot);
    cout << endl;

    /* 根节点只有一个key 不能再删了 */
    cout << "删除Z后:" << endl;
    deleteKey(pRoot, 'Z');
    DPSearch(pRoot);
    cout << endl;
#endif

#if 0
    /* (2) 测试删除的key 不在叶子节点的情况 */
    cout << "删除N后:" << endl;
    deleteKey(pRoot, 'N');
    DPSearch(pRoot);
    cout << endl;

    /* 删除N 后: 先从P的右子树找到叶子节点key 最小的Q, 
                              用Q 覆盖P, 然后相当于从叶子节点删除P.
                                          G      P
                                      /      |         \
                                    C        K       R  T  X
                                   / \      / \     / |  |  \ 
                                 A   DEF    H LM   Q  S  W  YZ
    */
    
    cout << "删除P后:" << endl;
    deleteKey(pRoot, 'P');
    DPSearch(pRoot);
    cout << endl;
    /* 删除P 后: 
                                          G      Q
                                      /      |         \
                                    C        K        T   X
                                   / \      / \      /  |  \ 
                                 A   DEF   H  LM   RS   W  YZ
    */

    cout << "删除K后:" << endl;
    deleteKey(pRoot, 'K');
    DPSearch(pRoot);
    cout << endl;
    /* 删除K 后: 
                                          G      Q
                                      /      |         \
                                    C        L        T   X
                                   / \      / \      /  |  \ 
                                 A   DEF   H   M    RS  W  YZ
    */
    
    cout << "删除L后:" << endl;
    deleteKey(pRoot, 'L');
    DPSearch(pRoot);
    cout << endl;
    /* 删除L 后: 
                                          G      T
                                      /       |         \
                                    C         Q          X
                                   / \      /   \       /  \ 
                                 A   DEF   HM   RS     W   YZ
    */

    cout << "删除G后:" << endl;
    deleteKey(pRoot, 'G');
    DPSearch(pRoot);
    cout << endl;
    /* 删除G 后:  用G 右子树最小的H 来替换,并从叶子节点
                               删除H 即可。
                                          H      T
                                      /       |         \
                                    C         Q          X
                                   / \      /   \       /  \ 
                                 A   DEF    M   RS     W   YZ
    */ 

    cout << "删除Q后:" << endl;
    deleteKey(pRoot, 'Q');
    DPSearch(pRoot);
    cout << endl;
    /* 删除Q 后:  
                                          H      T
                                      /       |         \
                                    C         R          X
                                   / \      /   \       /  \ 
                                 A   DEF    M    S     W   YZ
    */

    cout << "删除R后:" << endl;
    deleteKey(pRoot, 'R');
    DPSearch(pRoot);
    cout << endl;
    /* 删除R 后:  
                                          H     
                                      /        \
                                    C         T   X
                                   / \      /   |   \ 
                                 A   DEF   MS   W   YZ
    */

#endif    

    return 0;
}

运行结果:

(1)测试删除的key在叶子节点上:

《学习笔记:B树建立,搜索和删除操作》

(2)测试删除的key不是在叶子节点上:

《学习笔记:B树建立,搜索和删除操作》



    原文作者:B树
    原文地址: https://blog.csdn.net/u010312436/article/details/52037478
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注