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

来源:互联网 发布:覆盖法贪心算法 编辑:程序博客网 时间:2024/06/09 13:05

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;}@Overridepublic 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;}@Overridepublic 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,一个是retptrretptr.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.rKeyretval = 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树的删除非常复杂,因为要保证所有的叶结点都在同一层,所以删除的有些情况需要合并兄弟结点,参考书上也没讲,所以这个知识点就不深究了,等以后有时间再弄

原创粉丝点击