您的位置:首页 > 编程语言 > Java开发

2-3树--Java实现部分算法

2013-07-31 19:52 267 查看
2-3树是一种树结构类型的检索树,符合下面的定义:

1、一个节点包含一个或两个关键码

2、每个内部结点有两个子女(如果它包含一个关键码)或三个子女(如果它包含三个关键码)

3、所有叶结点都在树的同一层,因此树的高度总是平衡的

4、对于每个结点,其左子树的所有后继结点都小于第一个关键码的值,而其中间子树的所有结点的值都大于或等于第一个关键码的值,如果结点有右子树,那么其中间子树的所有后继结点的值都小于第二个关键码的值,而其右子树中的所有后继结点都大于或等于第二个关键码的值

定义一个结点类,它是树的内部类

private class TTNode
{
Object lKey;
Object rKey;
TTNode leftNode;
TTNode centNode;
TTNode rightNode;
public TTNode() {
leftNode = centNode = rightNode = null;
lKey = rKey = -1;
}

boolean isLeaf()
{
return leftNode == null;
}
}


也可以将叶结点和内部结点分开定义以节省空间

private interface VarTTNode
{
boolean ifLeaf();
}

private class LeafTTNode implements VarTTNode
{
Object llKey;
Object lrKey;

public LeafTTNode(Object lKey,Object rKey) {
llKey = lKey;
lrKey = rKey;
}

@Override
public boolean ifLeaf() {
return true;
}
}

private class IntlTTNode implements VarTTNode
{
Object ilKey;
Object irKey;
VarTTNode leftNode;
VarTTNode centNode;
VarTTNode rightNode;

public IntlTTNode(Object lKey,Object rKey,VarTTNode lNode, VarTTNode cNode,VarTTNode rNode) {
ilKey = lKey;
irKey = rKey;
leftNode = lNode;
centNode = cNode;
rightNode = rNode;
}
@Override
public boolean ifLeaf() {
return false;
}

}


作为检索树,最重要的是插入、查找和删除,为了简便,结点类的实现不分开。

代码实现查找

private boolean findHelp(TTNode subroot,Object key) {
if(subroot == null)
return false;
if((int)key == (int)subroot.lKey)
{
return true;
}
if((int)subroot.rKey != -1 && (int)key == (int)subroot.rKey)
{
return true;
}
if((int)key < (int)subroot.lKey)
{
return findHelp(subroot.leftNode, key);
}
else if((int)subroot.rKey == -1)
{
return findHelp(subroot.centNode, key);
}
else if((int)key < (int)subroot.rKey)
{
return findHelp(subroot.centNode, key);
}
else
{
return findHelp(subroot.rightNode, key);
}
}


往2-3树中插入数据较为复杂,如下分析

1、往2-3树中插入关键码如BST中一样,是放到相应的叶结点。

2、与BST不同的是,2-3树不创建子女结点来放置插入的记录,即2-3树不向下生长

3、第一步,首先找到要插入的叶结点的位置,如果该叶结点只有一个关键码,直接插入,不需要修改

4、如果该叶结点有两个关键码,那么就需要创建更多的空间,假设要插入的结点是N,按如下步骤分裂N

a、先把N分裂成两个结点,为N和N‘,N得到三个关键码中最小的值,N'得到最大的,中间的关键码值和N'一起传回父结点,这成为一次提升

b、如果父结点只有一个值,直接将中间的关键码插入,如果父结点已满,那么就重复进行分裂。

代码实现

首先实现分裂函数

//分裂函数参数subroot要分裂的结点,inval是要插入的值,也可以是下面升上来的值,
//inptr是下面升上来的子树,retptr是分裂的子树
public Object splitNode(TTNode subroot,Object inval,TTNode inptr,TTNode retptr)
{
retptr = new TTNode();
Object retval;
if((int)inval < (int)subroot.lKey)//subroot.lKey是中间值
{
retval = subroot.lKey;//把中间值传递给返回值
subroot.lKey = inval;//把当前结点分成两个结点,一个是subroot,一个是retptr
retptr.lKey = subroot.rKey;//吧subroot的右值赋给retptr的左值
retptr.leftNode = subroot.centNode;//把subroot的中子树赋给retptr的左子树
retptr.centNode = subroot.rightNode;//把subroot的右子树赋给retptr的中子树
subroot.centNode = inptr;//把下面升上来的子树赋值给subroot的中子树
subroot.rKey = -1;//把subroot的右值清空
return retval;
}
else if ((int)inval < (int)subroot.rKey)//中间值是inval,即要插入的值
{
retval = inval;
retptr.lKey = subroot.rKey;//吧subroot的右值赋给retptr的左值
retptr.leftNode = inptr;//把下面升上来的子树赋值给retptr的左子树
retptr.centNode = subroot.rightNode;//
subroot.rKey = -1;//把subroot的右值清空
return retval;
}
else {//中间值是subroot.rKey
retval = subroot.rKey;
retptr.lKey = inval;
retptr.leftNode = subroot.rightNode;
retptr.centNode = inptr;
subroot.rKey = -1;
return retval;
}
}


返回值是用来传递给父结点的中值

插入函数

//subroot是要插入的叶结点,key是插入的关键码,retptr用来指定子树穿上来的值
public Object insertHelp(TTNode subroot,Object key,TTNode retptr)
{
Object myretv = null;
TTNode myretp = null;
if(subroot == null)//如果树是空的,初始化一个结点
{
subroot = new TTNode();
subroot.lKey = key;
}
else if(subroot.isLeaf())//如果当前结点是叶结点
{
if((int)subroot.rKey == -1)//右值为空
{
if((int)key > (int)subroot.lKey)//插入关键码大于左值
{
subroot.rKey = key;//直接插入到右值
}
else //否则
{
subroot.rKey = subroot.lKey;//左值赋值给右值
subroot.lKey = key;//插入到左值
}
}
else //右值不为空
{
myretv = splitNode(subroot, key, null, retptr);//分裂该结点
}
}
else if((int)key < (int)subroot.lKey)//如果待插入小于左值
{
myretv = insertHelp(subroot.leftNode, key, myretp);//那么要插入的叶结点在它的左子树上
}
else if((int)subroot.rKey == -1 || (int)key < (int)subroot.rKey)//如果当前节点右值为空或待插入值小于右值
{
myretv = insertHelp(subroot.centNode, key, myretp);//那么要插入的叶结点在它的中子树上
}
else //其他
{
myretv = insertHelp(subroot.rightNode, key, myretp);//那么要插入的叶结点在它的右子树上
}

if(myretp != null)//如果下面传递过来的分裂子树不为空
{
if((int)subroot.rKey != -1)//如果右值不为空
{
myretv = splitNode(subroot,myretv, myretp, retptr);//分裂当前节点
}
else {//否则,即右值为空
if((int)myretv < (int)subroot.lKey)//传上来的中值小于当前结点的左值
{
subroot.rKey = subroot.lKey;//将当前结点的左值赋值给右值
subroot.lKey = myretv;//将传上来的值赋值给左值
subroot.rightNode = subroot.centNode;//将中子树赋给右子树
subroot.centNode = myretp;//将传上来的分裂子树赋值给中子树
}
else {//否则,即传上来的值大于等于左值
subroot.rKey = myretv;//直接将传上来的值赋值给右值
subroot.rightNode = myretp;//传上来的分裂结点赋值给右结点
}
}
}
return myretv;
}


2-3树的删除非常复杂,因为要保证所有的叶结点都在同一层,所以删除的有些情况需要合并兄弟结点,参考书上也没讲,所以这个知识点就不深究了,等以后有时间再弄
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: