【算法导论】B树

      一棵B树T是具有如下性质的有根树(设根为root):

1.每个节点x有一下域:

  (a)num,当前存储在节点x的关键字个数,关键字以非降序存放,因此key[i]<=key[i+1]<=……key[n];

  (b)isleaf,是一个bool值,如果x为叶子节点,则isleaf=true.

  (c)每个节点包括num+1个指向其子女的指针p[0],p[1],……p[num]。如果x为叶子,则p=NULL

  (d)每个节点包括num个关键字key[0],key[1],……key[num-1]。各关键字key[i]对存储在各子树中的关键字范围加以分隔:  k1<=key[1]<=k2<=key[2]……

2.每个叶节点具有相同的深度。

3.每一个节点包含的关键字有上下界。这些界可以用一个称为B树的最小度数的固定整数M>=2来表示。每个非根节点的个数n必须满足M-1<=n<=2M-1。根节点至少包括    一个关键字。如果一个节点是满的,则它恰好有2M-1个关键字。

 
     一棵B树可以表示如下:

《【算法导论】B树》

        B树与红黑树的相似之处在于,每棵有n个节点的B树高度为O(lgn),但可能要比一棵红黑树的高度小很多,因为它的分支比较多!因为在磁盘存储中,需要经常读取数据,所以选择一个大的分支因子,可以大大地降低树的高度,以及磁盘存取次数。这样说可能比较抽象,下面举例说明:下图为一棵分支因子为1001、高度为2的B树,可以看出它可以存储超过10亿个关键字;但是,因为根节点可以持久的保留在主存中,因此需找某个关键字至多只需要两次磁盘存取。如果用二叉树存储的话,树的深度将会很大,那么寻找位于叶子节点处的关键字将需要很多次磁盘读取!假设有n个节点,那么二叉树的高度为h<=lg(n+1),而B树为h<=log((n+1)/2)/log(M),其中M为最小度数。

《【算法导论】B树》

B树的各种操作:

1.查找

    查找b树和查找二叉树类似,就是在分支处进行判断选择正确的子树,然后递归调用。查找过程的时间复杂读为Mlgn/lgM具体程序实现如下:

/**********************************************************\
函数功能:查找关键字所在的节点
输入:    树的根,关键字
输出:    关键字所在的节点
\**********************************************************/
BtreeNode *BtreeSearch(BtreeNode *TestNode,int keyword)
{
	int i=0;
	while(i<TestNode->num&&keyword>TestNode->key[i])
		i=i+1;
	if(i<=TestNode->num&&keyword==TestNode->key[i])
		return TestNode;
	if(TestNode->isleaf)
	{
		printf("Not founded!\n");
		return NULL;
	}
	else
	{
		return BtreeSearch(TestNode->p[i],keyword);
	}
}

2.创建空的B树

/**********************************************************\
函数功能:创建节点
输入:无
输出:新节点
\**********************************************************/
BtreeNode * BtreeCreate()
{
	BtreeNode *node=(BtreeNode *)malloc(sizeof(BtreeNode));
	if(NULL==node)
		return NULL;
	node->isleaf=true;
	node->num=0;
	for(int i=0;i<2*M;i++)
		node->p[i]=NULL;
	for(int i=0;i<2*M-1;i++)
		node->key[i]=0;
	return node;
}

3.插入

        B树的插入比二叉树的插入要复杂的多,因为二叉树的插入是插入新的节点,而B树的插入是将关键字插入到已存在的节点,而节点可能已经是满节点(前面提到过),就会破坏B树的性质。因此不能将关键字插入到满节点上。根据B树的规则,每个节点的关键字个数在[M-1, 2M-1]之间,故当keyword(要插入的关键字)要加入到某个叶子时,如果该叶子节点已经有2M-1个关键字,则再加入keyword就违反了B树的定义,这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含M-1个关键字的子节点,同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的关键字个数超过2M-1,则要继续向上分裂,直到根节点,根节点的分裂会使得树加高一层。

        为了解决上面问题,我们需要不断地回溯,这显然比较复杂,我们可以未雨绸缪:我们不是等到发现是否真的需要分裂一个满节点时才做插入操作。相反地,当沿着树向下查找要插入关键字所处位置时,就分裂沿途遇到的每个满节点。这样做后,每当要分裂一个满节点时,就能保证其双亲不是满节点。

分裂满节点的过程图解如下:

《【算法导论】B树》

        我们还要考虑特殊情况:当分裂一个满的根时,需要先让根成为一个新的空根节点的孩子,这样才能被上面的分解过程分解。树的高度增加1,分裂是树增高的唯一途径!其操作如下图:

《【算法导论】B树》

综上所述,插入过程的具体实现如下:

//////////////////////////////插入部分///////////////////////////////////////////
/**********************************************************\
函数功能:节点分裂,防止违反B树的性质
输入: 父节点father ,子节点child,k表示子节点为父节点的哪个孩子
输出:无
\**********************************************************/
void BtreeSplitChild(BtreeNode *father,BtreeNode *child,int k)
{
	BtreeNode *newchild=(BtreeNode *)malloc(sizeof(BtreeNode));
	newchild->isleaf=child->isleaf;//newchild为child的右节点,即child分裂为child和newchild
	newchild->num=M-1;
	for(int i=0;i<M-1;i++)
		newchild->key[i]=child->key[i+M];
	if(!child->isleaf)//当child不是叶子时,还要把指针赋给newchild
	{
		for(int j=0;j<M;j++)
			newchild->p[j]=child->p[j+M];
	}

	child->num=M-1;//child的个数由2M-1变为M-1

	for(int i=father->num-1;i>=k+1;i--)//改变父节点的内容
		father->p[i+1]=father->p[i];
	father->p[k+1]=newchild;
	for(int j=father->num-1;j>=k;j--)
		father->key[j+1]=father->key[j];
	father->key[k]=child->key[M-1];//将child的中间节点提升到父节点
	father->num=father->num+1;


}

/**********************************************************\
函数功能:x节点不是满的情况下,插入keyword
输入:B树的根,要插入的关键字
输出:无
\**********************************************************/
void BtreeInsertNotFull(BtreeNode *x,int keyword)
{
	int i=x->num;
	if(x->isleaf)//当x为叶子时,keyword插入到该节点中
	{
		while(i>=1&&keyword<x->key[i-1])
		{
			x->key[i]=x->key[i-1];
			i=i-1;
		}	
		x->key[i]=keyword;
		x->num=x->num+1;
	}
	else//当x不是叶子时,找到keyword要插入的节点并插入
	{
		while(i>=1&&keyword<x->key[i-1])
		{
			i=i-1;
		}
	
		if(x->p[i]->num==2*M-1)//当节点为满节点时,需要分裂
		{
			BtreeSplitChild(x,x->p[i],i);
			if(keyword>x->key[i])
				i=i+1;
			
		}
		BtreeInsertNotFull(x->p[i],keyword);
	}
}

/**********************************************************\
函数功能:插入关键值
输入:B树的根,关键字
输出:B树的根
\**********************************************************/
BtreeNode * BtreeInsert(BtreeNode *TestNode,int keyword)
{
	if(TestNode->num==2*M-1)//当根节点为满时,唯一增加高度的情况
	{
		BtreeNode *newroot=(BtreeNode *)malloc(sizeof(BtreeNode));
		newroot->isleaf=false;//产生新的根
		newroot->num=0;
		newroot->p[0]=TestNode;
		BtreeSplitChild(newroot,TestNode,0);
		BtreeInsertNotFull(newroot,keyword);
		return newroot;
	}
	else
	{
		BtreeInsertNotFull(TestNode,keyword);
		return TestNode;
	}

}

4.删除

               B树的删除比插入操作更加复杂,插入操作只需考虑三种情况,而删除操作需要考虑的情况很多,情况如下:

《【算法导论】B树》

        和插入操作类似,根据B树的规则,每个节点的关键字个数在[M-1, 2M-1]之间,故当keyword(要插入的关键字)要从某个叶子删除时,如果该叶子节点只有有M-1个关键字,则再删除keyword就违反了B树的定义,这时就需要对该叶子节点进行合并。上图中各种情况中的t就是我所说的M即最小度数。具体程序实现如下:

///////////////////////////删除部分//////////////////////////////////////////
/**********************************************************\
函数功能:合并左右子节点
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeMergeChild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    // 将z中节点拷贝到y的后半部分
    y->num = 2 * M - 1;
    for(int i = M; i < 2 * M - 1; i++) 
	{
        y->key[i] = z->key[i-M];
    }
    y->key[M-1] = root->key[pos]; // 将root->key[pos]下降为y的中间节点
    
    
    if(false == z->isleaf)// 如果z是内节点即非叶子,需要拷贝指向子节点的指针p
	{
        for(int i = M; i < 2 * M; i++) 
		{
            y->p[i] = z->p[i-M];
        }
    }
     
      
    for(int j = pos + 1; j < root->num; j++) // root->key[pos]下降到y中,更新root中key和p
	{
        root->key[j-1] = root->key[j];
        root->p[j] = root->p[j+1];
    }

    root->num -= 1;
    free(z);
}
  
/**********************************************************\
函数功能:删除关键字keyword
输入:树的根,关键字
输出:树的根
\**********************************************************/
BtreeNode *BtreeDelete(BtreeNode *root, int keyword)
{
    
    // 唯一能降低树高的情形
    if(1 == root->num) // 当根只有一个关键字,两个子女
	{
        BtreeNode *y = root->p[0];
        BtreeNode *z = root->p[1];
        if(NULL != y && NULL != z &&M - 1 == y->num && M - 1 == z->num)//两个子女的关键字个数都为M-1时,合并根与两个子女
		{
            BtreeMergeChild(root, 0, y, z);
            free(root);//注意释放空间
            BtreeDeleteNotFull(y, keyword);
            return y;
        }
		else 
		{
            BtreeDeleteNotFull(root, keyword);
            return root;
        }
    } 
	else 
	{
        BtreeDeleteNotFull(root, keyword);    
        return root;
    }
}

/**********************************************************\
函数功能: root至少有个M个关键字时删除关键字
输入:   树的根,关键字
输出:   无
\**********************************************************/
void BtreeDeleteNotFull(BtreeNode *root, int keyword)
{
    if(true == root->isleaf) // 如果在叶子节点,直接删除,情况1
	{ 
        int i = 0;
        while(i < root->num && keyword > root->key[i]) i++;
        if(keyword == root->key[i])
		{
            for(int j = i + 1; j < 2 * M - 1; j++) 
			{
                root->key[j-1] = root->key[j];
            }
            root->num -= 1;
        } 
		else 
		{
            printf("keyword not found\n");
        }
    }
	else 
	{  // 在分支中
        int i = 0;
        BtreeNode *y = NULL, *z = NULL;
        while(i < root->num && keyword > root->key[i]) i++; 
        if(i < root->num && keyword == root->key[i]) 
		{ // 如果在分支节点找到keyword
            y = root->p[i];
            z = root->p[i+1];
            if(y->num > M - 1) 
			{  
		      // 如果左分支关键字多于M-1,则找到左分支的最右节点pre,替换keyword
                // 并在左分支中递归删除prev,情况2a
                int pre = BtreeSearchPrevious(y);
                root->key[i] = pre;
                BtreeDeleteNotFull(y, pre);//递归处理
            } 
			else if(z->num > M - 1)
			{
                // 如果右分支关键字多于M-1,则找到右分支的最左节点next,替换keyword
                // 并在右分支中递归删除next,情况2b
                int next = BtreeSearchNext(z);
                root->key[i] = next;
                BtreeDeleteNotFull(z, next);
            }
			else // 两个分支节点数都为M-1,则合并至y,并在y中递归删除keyword,情况2c
			{
                
                BtreeMergeChild(root, i, y, z);
                BtreeDelete(y, keyword);
            }
        }
		else// 分支中没有,在分支的子节点中的情况
		{   
            y = root->p[i];
            if(i < root->num) 
			{
                z = root->p[i+1];//y的右兄弟
            }
            BtreeNode *p = NULL;//初始化
            if(i > 0)
			{
                p = root->p[i-1];//y的左兄弟
            }

            if(y->num == M - 1)
			{
                if(i > 0 && p->num > M - 1) 
				{
                    // 左兄弟节点关键字个数大于M-1,情况3a
                    BtreeChangeToRchild(root, i-1, p, y); 
                } 
				else if(i < root->num && z->num > M - 1) 
				{
                    // 右兄弟节点关键字个数大于M-1,情况3a
                    BtreeChangeToLchild(root, i, y, z); 
                }
				else if(i > 0) 
				{   
                    BtreeMergeChild(root, i-1, p, y);  //左右兄弟节点都不大于M-1,情况3b
                    y = p;
                } 
				else //没有左兄弟的情况
				{
                    BtreeMergeChild(root, i, y, z); 
                }
                BtreeDeleteNotFull(y, keyword);
            }
			else 
			{
                BtreeDeleteNotFull(y, keyword);
            }
        }

    }
}

/**********************************************************\
函数功能:寻找以root为根的最大关键字
输入:    树的根
输出:    最大关键字
\**********************************************************/ 
int BtreeSearchPrevious(BtreeNode *root)
{
    BtreeNode *y = root;
    while(false == y->isleaf)
	{
        y = y->p[y->num];
    }
    return y->key[y->num-1];
}

/**********************************************************\
函数功能:寻找以root为根的最小关键字
输入:树的根
输出:最小关键字
\**********************************************************/
int BtreeSearchNext(BtreeNode *root)  
{
    BtreeNode *z = root;
    while(false == z->isleaf)
	{
        z = z->p[0];
    }
    return z->key[0];
}


/**********************************************************\
函数功能:z向y借节点,将root->key[pos]下降至z,将y的最大关键字上升至root的pos处
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeChangeToRchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    z->num += 1;
    for(int i = z->num -1; i > 0; i--) 
	{
        z->key[i] = z->key[i-1];
    }
    z->key[0]= root->key[pos];
    root->key[pos] = y->key[y->num-1];

    if(false == z->isleaf)
	{
        for(int i = z->num; i > 0; i--) 
		{
            z->p[i] = z->p[i-1];
        }
        z->p[0] = y->p[y->num];
    }

    y->num -= 1; 
}

 
/**********************************************************\
函数功能:y向借节点,将root->key[pos]下降至y,将z的最小关键字上升至root的pos处
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeChangeToLchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    y->num += 1;
    y->key[y->num-1] = root->key[pos];
    root->key[pos] = z->key[0];

    for(int j = 1; j < z->num; j++)
	{
        z->key[j-1] = z->key[j];
    }

    if(false == z->isleaf) 
	{
        y->p[y->num] = z->p[0];
        for(int j = 1; j <= z->num; j++) 
		{
            z->p[j-1] = z->p[j];
        }
    } 

    z->num -= 1;
}

下面用具体实例来形象地说明B树的操作:

假设初始的B树如下:

《【算法导论】B树》

经过一系列的插入操作后:

《【算法导论】B树》

《【算法导论】B树》

在程序中为表示方便,将关键字由字母换成了数字,A、B、C……Y、Z对应于1、2、3……25、26.

经过上面的插入操作后,紧接在进行一系列删除操作:

《【算法导论】B树》

《【算法导论】B树》

《【算法导论】B树》

具体的完整实例程序实现如下:

#include<stdio.h>
#include<stdlib.h>

#define M 3

//节点结构体
typedef struct BtreeNode
{
	int num;
	struct BtreeNode *p[2*M];
	int key[2*M-1];
	bool isleaf; 
}BtreeNode;

BtreeNode * BtreeCreate();
void BtreeSplitChild(BtreeNode *father,BtreeNode *child,int k);
void BtreeInsertNotFull(BtreeNode *x,int keyword);
BtreeNode * BtreeInsert(BtreeNode *TestNode,int keyword);
BtreeNode *BtreeSearch(BtreeNode *TestNode,int keyword);

//////
void BtreeMergeChild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z);
BtreeNode *BtreeDelete(BtreeNode *root, int keyword);
void BtreeDeleteNotFull(BtreeNode *root, int keyword);
int BtreeSearchPrevious(BtreeNode *root);
int BtreeSearchNext(BtreeNode *root);
void BtreeChangeToRchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z);
void BtreeChangeToLchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z);

/**********************************************************\
函数功能:创建节点
输入:无
输出:新节点
\**********************************************************/
BtreeNode * BtreeCreate()
{
	BtreeNode *node=(BtreeNode *)malloc(sizeof(BtreeNode));
	if(NULL==node)
		return NULL;
	node->isleaf=true;
	node->num=0;
	for(int i=0;i<2*M;i++)
		node->p[i]=NULL;
	for(int i=0;i<2*M-1;i++)
		node->key[i]=0;
	return node;
}
//////////////////////////////插入部分///////////////////////////////////////////
/**********************************************************\
函数功能:节点分裂,防止违反B树的性质
输入: 父节点father ,子节点child,k表示子节点为父节点的哪个孩子
输出:无
\**********************************************************/
void BtreeSplitChild(BtreeNode *father,BtreeNode *child,int k)
{
	BtreeNode *newchild=(BtreeNode *)malloc(sizeof(BtreeNode));
	newchild->isleaf=child->isleaf;//newchild为child的右节点,即child分裂为child和newchild
	newchild->num=M-1;
	for(int i=0;i<M-1;i++)
		newchild->key[i]=child->key[i+M];
	if(!child->isleaf)//当child不是叶子时,还要把指针赋给newchild
	{
		for(int j=0;j<M;j++)
			newchild->p[j]=child->p[j+M];
	}

	child->num=M-1;//child的个数由2M-1变为M-1
	
	for(int i=father->num;i>=k+1;i--)//改变父节点的内容
	{
		father->p[i+1]=father->p[i];
	}
	father->p[k+1]=newchild;
	for(int j=father->num-1;j>=k;j--)
		father->key[j+1]=father->key[j];
	father->key[k]=child->key[M-1];//将child的中间节点提升到父节点
	father->num=father->num+1;


}

/**********************************************************\
函数功能:x节点不是满的情况下,插入keyword
输入:B树的根,要插入的关键字
输出:无
\**********************************************************/
void BtreeInsertNotFull(BtreeNode *x,int keyword)
{
	int i=x->num;
	if(x->isleaf)//当x为叶子时,keyword插入到该节点中
	{
		while(i>=1&&keyword<x->key[i-1])
		{
			x->key[i]=x->key[i-1];
			i=i-1;
		}	
		x->key[i]=keyword;
		x->num=x->num+1;
	}
	else//当x不是叶子时,找到keyword要插入的节点并插入
	{
		i=x->num;
		while(i>=1&&keyword<x->key[i-1])
		{
			i=i-1;
		}
	
		if(x->p[i]->num==2*M-1)//当节点为满节点时,需要分裂
		{
			BtreeSplitChild(x,x->p[i],i);
			if(keyword>x->key[i])
				i=i+1;
			
		}
		BtreeInsertNotFull(x->p[i],keyword);
	}
}

/**********************************************************\
函数功能:插入关键值
输入:B树的根,关键字
输出:B树的根
\**********************************************************/
BtreeNode * BtreeInsert(BtreeNode *TestNode,int keyword)
{
	if(TestNode->num==2*M-1)//当根节点为满时,唯一增加高度的情况
	{
		
		BtreeNode *newroot=(BtreeNode *)malloc(sizeof(BtreeNode));
		newroot->isleaf=false;//产生新的根
		newroot->num=0;
		newroot->p[0]=TestNode;
		BtreeSplitChild(newroot,TestNode,0);
		BtreeInsertNotFull(newroot,keyword);
		return newroot;
	}
	else
	{
		
		BtreeInsertNotFull(TestNode,keyword);
		return TestNode;
	}

}

/**********************************************************\
函数功能:查找关键字所在的节点
输入:    树的根,关键字
输出:    关键字所在的节点
\**********************************************************/
BtreeNode *BtreeSearch(BtreeNode *TestNode,int keyword)
{
	int i=0;
	while(i<TestNode->num&&keyword>TestNode->key[i])
		i=i+1;
	if(i<=TestNode->num&&keyword==TestNode->key[i])
		return TestNode;
	if(TestNode->isleaf)
	{
		printf("Not founded!\n");
		return NULL;
	}
	else
	{
		return BtreeSearch(TestNode->p[i],keyword);
	}
}




///////////////////////////删除部分//////////////////////////////////////////
/**********************************************************\
函数功能:合并左右子节点
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeMergeChild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    // 将z中节点拷贝到y的后半部分
    y->num = 2 * M - 1;
    for(int i = M; i < 2 * M - 1; i++) 
	{
        y->key[i] = z->key[i-M];
    }
    y->key[M-1] = root->key[pos]; // 将root->key[pos]下降为y的中间节点
    
    
    if(false == z->isleaf)// 如果z是内节点即非叶子,需要拷贝指向子节点的指针p
	{
        for(int i = M; i < 2 * M; i++) 
		{
            y->p[i] = z->p[i-M];
        }
    }
     
      
    for(int j = pos + 1; j < root->num; j++) // root->key[pos]下降到y中,更新root中key和p
	{
        root->key[j-1] = root->key[j];
        root->p[j] = root->p[j+1];
    }

    root->num -= 1;
    free(z);
}
  
/**********************************************************\
函数功能:删除关键字keyword
输入:树的根,关键字
输出:树的根
\**********************************************************/
BtreeNode *BtreeDelete(BtreeNode *root, int keyword)
{
    
    // 唯一能降低树高的情形
    if(1 == root->num) // 当根只有一个关键字,两个子女
	{
        BtreeNode *y = root->p[0];
        BtreeNode *z = root->p[1];
        if(NULL != y && NULL != z &&M - 1 == y->num && M - 1 == z->num)//两个子女的关键字个数都为M-1时,合并根与两个子女
		{
            BtreeMergeChild(root, 0, y, z);
            free(root);//注意释放空间
            BtreeDeleteNotFull(y, keyword);
            return y;
        }
		else 
		{
            BtreeDeleteNotFull(root, keyword);
            return root;
        }
    } 
	else 
	{
        BtreeDeleteNotFull(root, keyword);    
        return root;
    }
}

/**********************************************************\
函数功能: root至少有个M个关键字时删除关键字
输入:   树的根,关键字
输出:   无
\**********************************************************/
void BtreeDeleteNotFull(BtreeNode *root, int keyword)
{
    if(true == root->isleaf) // 如果在叶子节点,直接删除,情况1
	{ 
        int i = 0;
        while(i < root->num && keyword > root->key[i]) i++;
        if(keyword == root->key[i])
		{
            for(int j = i + 1; j < 2 * M - 1; j++) 
			{
                root->key[j-1] = root->key[j];
            }
            root->num -= 1;
        } 
		else 
		{
            printf("keyword not found\n");
        }
    }
	else 
	{  // 在分支中
        int i = 0;
        BtreeNode *y = NULL, *z = NULL;
        while(i < root->num && keyword > root->key[i]) i++; 
        if(i < root->num && keyword == root->key[i]) 
		{ // 如果在分支节点找到keyword
            y = root->p[i];
            z = root->p[i+1];
            if(y->num > M - 1) 
			{  
		      // 如果左分支关键字多于M-1,则找到左分支的最右节点pre,替换keyword
                // 并在左分支中递归删除prev,情况2a
                int pre = BtreeSearchPrevious(y);
                root->key[i] = pre;
                BtreeDeleteNotFull(y, pre);//递归处理
            } 
			else if(z->num > M - 1)
			{
                // 如果右分支关键字多于M-1,则找到右分支的最左节点next,替换keyword
                // 并在右分支中递归删除next,情况2b
                int next = BtreeSearchNext(z);
                root->key[i] = next;
                BtreeDeleteNotFull(z, next);
            }
			else // 两个分支节点数都为M-1,则合并至y,并在y中递归删除keyword,情况2c
			{
                
                BtreeMergeChild(root, i, y, z);
                BtreeDelete(y, keyword);
            }
        }
		else// 分支中没有,在分支的子节点中的情况
		{   
            y = root->p[i];
            if(i < root->num) 
			{
                z = root->p[i+1];//y的右兄弟
            }
            BtreeNode *p = NULL;//初始化
            if(i > 0)
			{
                p = root->p[i-1];//y的左兄弟
            }

            if(y->num == M - 1)
			{
                if(i > 0 && p->num > M - 1) 
				{
                    // 左兄弟节点关键字个数大于M-1,情况3a
                    BtreeChangeToRchild(root, i-1, p, y); 
                } 
				else if(i < root->num && z->num > M - 1) 
				{
                    // 右兄弟节点关键字个数大于M-1,情况3a
                    BtreeChangeToLchild(root, i, y, z); 
                }
				else if(i > 0) 
				{   
                    BtreeMergeChild(root, i-1, p, y);  //左右兄弟节点都不大于M-1,情况3b
                    y = p;
                } 
				else //没有左兄弟的情况
				{
                    BtreeMergeChild(root, i, y, z); 
                }
                BtreeDeleteNotFull(y, keyword);
            }
			else 
			{
                BtreeDeleteNotFull(y, keyword);
            }
        }

    }
}

/**********************************************************\
函数功能:寻找以root为根的最大关键字
输入:    树的根
输出:    最大关键字
\**********************************************************/ 
int BtreeSearchPrevious(BtreeNode *root)
{
    BtreeNode *y = root;
    while(false == y->isleaf)
	{
        y = y->p[y->num];
    }
    return y->key[y->num-1];
}

/**********************************************************\
函数功能:寻找以root为根的最小关键字
输入:树的根
输出:最小关键字
\**********************************************************/
int BtreeSearchNext(BtreeNode *root)  
{
    BtreeNode *z = root;
    while(false == z->isleaf)
	{
        z = z->p[0];
    }
    return z->key[0];
}


/**********************************************************\
函数功能:z向y借节点,将root->key[pos]下降至z,将y的最大关键字上升至root的pos处
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeChangeToRchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    z->num += 1;
    for(int i = z->num -1; i > 0; i--) 
	{
        z->key[i] = z->key[i-1];
    }
    z->key[0]= root->key[pos];
    root->key[pos] = y->key[y->num-1];

    if(false == z->isleaf)
	{
        for(int i = z->num; i > 0; i--) 
		{
            z->p[i] = z->p[i-1];
        }
        z->p[0] = y->p[y->num];
    }

    y->num -= 1; 
}

 
/**********************************************************\
函数功能:y向借节点,将root->key[pos]下降至y,将z的最小关键字上升至root的pos处
输入:根,左右子节点,左节点是父节点的第pos个节点
输出:无
\**********************************************************/
void BtreeChangeToLchild(BtreeNode *root, int pos, BtreeNode *y, BtreeNode *z)
{
    y->num += 1;
    y->key[y->num-1] = root->key[pos];
    root->key[pos] = z->key[0];

    for(int j = 1; j < z->num; j++)
	{
        z->key[j-1] = z->key[j];
    }

    if(false == z->isleaf) 
	{
        y->p[y->num] = z->p[0];
        for(int j = 1; j <= z->num; j++) 
		{
            z->p[j-1] = z->p[j];
        }
    } 

    z->num -= 1;
}


//按层次遍历B树
void Print(BtreeNode *root)
{	
	int front,rear;
	int num=0;
	int num1=0;
	int flag=0;
	BtreeNode *queue[100];
	BtreeNode *s;
	if(root!=NULL)
	{
		rear=1;
		front=0;
		queue[rear]=root;
		while(front<rear)
		{
			front++;
		
			s=queue[front];

			if(!s->isleaf)
			{
				for(int j=0;j<=s->num;j++)
				{
					if(s->p[j]!=NULL)
					{
						rear++;
						queue[rear]=s->p[j];
						
					}
				}
			}
			
		}
			for(int k=1;k<=rear;k++)//使输出简单易看
			{	
				for(int i=0;i<queue[k]->num;i++)
					printf("%d ",queue[k]->key[i]);
				printf("| ");			
				if(k>num)
				{
					
					while(flag<k)
					{
					num=num+queue[flag+1]->num+1;
					flag++;
					}
				printf("\n");
				flag=k;
				}
				
			}
			
			
		
	}
}


////////////////////////////////////////////////////////////////////////////
void main()
{
/**************************初始化**************************/
	BtreeNode *TestNode=BtreeCreate();
	BtreeNode *Node1=BtreeCreate();
	BtreeNode *Node2=BtreeCreate();
	BtreeNode *Node3=BtreeCreate();
	BtreeNode *Node4=BtreeCreate();
	BtreeNode *Node5=BtreeCreate();
	BtreeNode *root=BtreeCreate();

	BtreeNode *SearchNode=BtreeCreate();
	TestNode->isleaf=false;
	TestNode->num=4;
	TestNode->key[0]=7;
	TestNode->key[1]=13;
	TestNode->key[2]=16;
	TestNode->key[3]=24;
	TestNode->p[0]=Node1;
	TestNode->p[1]=Node2;
	TestNode->p[2]=Node3;
	TestNode->p[3]=Node4;
	TestNode->p[4]=Node5;

	Node1->isleaf=true;
	Node1->num=4;
	Node1->key[0]=1;
	Node1->key[1]=3;
	Node1->key[2]=4;
	Node1->key[3]=5;

	Node2->isleaf=true;
	Node2->num=2;
	Node2->key[0]=10;
	Node2->key[1]=11;


	Node3->isleaf=true;
	Node3->num=2;
	Node3->key[0]=14;
	Node3->key[1]=15;


	Node4->isleaf=true;
	Node4->num=5;
	Node4->key[0]=18;
	Node4->key[1]=19;
	Node4->key[2]=20;
	Node4->key[3]=21;
	Node4->key[4]=22;


	Node5->isleaf=true;
	Node5->num=2;
	Node5->key[0]=25;
	Node5->key[1]=26;
	root=TestNode;
/*******************************初始化结束***********************/
	printf("原始B树:\n");
	Print(root);
	root=BtreeInsert(root,2);
	printf("\n插入关键字为2后的B树:\n");
	Print(root);
	root=BtreeInsert(root,17);
	printf("\n插入关键字为17后的B树:\n");
	Print(root);
	root=BtreeInsert(root,12);
	printf("\n插入关键字为12后的B树:\n");
	Print(root);
	root=BtreeInsert(root,6);
	printf("\n插入关键字为6后的B树:\n");
	Print(root);
	printf("\n\n");
	//删除操作
    root=BtreeDelete(root,6);
	printf("\n删除关键字为6后的B树:\n");
	Print(root);

	root=BtreeDelete(root,13);
	printf("\n删除关键字为13后的B树:\n");
	Print(root);

	root=BtreeDelete(root,7);
	printf("\n删除关键字为7后的B树:\n");
	Print(root);

	root=BtreeDelete(root,4);
	printf("\n删除关键字为4后的B树:\n");
	Print(root);


    root=BtreeDelete(root,2);
	printf("\n删除关键字为2后的B树:\n");
	Print(root);
	
}

程序结果如下(| 用于分开不同节点的关键字,下图显示是按树的层次遍历的,可以看出结果与上面插入和删除的图解过程完全相同!):

《【算法导论】B树》

注:如果程序出错,可能是使用的开发平台版本不同,请点击如下链接: 解释说明

原文:http://blog.csdn.net/tengweitw/article/details/17055797

作者:nineheadedbird

 

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