2-3树--Java实现部分算法
2013-07-31 19:52
267 查看
2-3树是一种树结构类型的检索树,符合下面的定义:
1、一个节点包含一个或两个关键码
2、每个内部结点有两个子女(如果它包含一个关键码)或三个子女(如果它包含三个关键码)
3、所有叶结点都在树的同一层,因此树的高度总是平衡的
4、对于每个结点,其左子树的所有后继结点都小于第一个关键码的值,而其中间子树的所有结点的值都大于或等于第一个关键码的值,如果结点有右子树,那么其中间子树的所有后继结点的值都小于第二个关键码的值,而其右子树中的所有后继结点都大于或等于第二个关键码的值
定义一个结点类,它是树的内部类
也可以将叶结点和内部结点分开定义以节省空间
作为检索树,最重要的是插入、查找和删除,为了简便,结点类的实现不分开。
代码实现查找
往2-3树中插入数据较为复杂,如下分析
1、往2-3树中插入关键码如BST中一样,是放到相应的叶结点。
2、与BST不同的是,2-3树不创建子女结点来放置插入的记录,即2-3树不向下生长
3、第一步,首先找到要插入的叶结点的位置,如果该叶结点只有一个关键码,直接插入,不需要修改
4、如果该叶结点有两个关键码,那么就需要创建更多的空间,假设要插入的结点是N,按如下步骤分裂N
a、先把N分裂成两个结点,为N和N‘,N得到三个关键码中最小的值,N'得到最大的,中间的关键码值和N'一起传回父结点,这成为一次提升
b、如果父结点只有一个值,直接将中间的关键码插入,如果父结点已满,那么就重复进行分裂。
代码实现
首先实现分裂函数
返回值是用来传递给父结点的中值
插入函数
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树的删除非常复杂,因为要保证所有的叶结点都在同一层,所以删除的有些情况需要合并兄弟结点,参考书上也没讲,所以这个知识点就不深究了,等以后有时间再弄
相关文章推荐
- 用代码实现二叉树的遍历-Java经典面试题算法部分核心
- 四分位算法中的一种java代码实现
- 二、Cocos2dx中Android部分的c++和java实现相互调用(高级篇)
- java实现的18位身份证格式验证算法
- 对sql查询语句组合查询的通用实现算法(c++版,java版)
- 算法基础——十种常用排序算法的Java及Python实现
- 排序(快排,冒泡,堆排序,插入排序,归并排序,选择排序)算法Java实现
- 【LeetCode-面试算法经典-Java实现】【029-Divide Two Integers(两个整数相除)】
- Java实现LRU算法
- Java实现常用权限控制算法
- 【LeetCode-面试算法经典-Java实现】【066-Plus One(加一)】
- 操作系统进程调度算法(Java 实现)
- KNN算法之JAVA实现
- 【密码学】DES加解密原理及其Java实现算法
- 算法java实现--回溯法--旅行售货员问题--排列树
- 【算法学习】快速排序算法实现(Java)
- Java实现以字符串某一位置为界,将它之前的部分与之后的部分互换位置
- 八皇后问题 java实现,算法两则
- 算法(第四版)学习笔记之java实现堆排序
- 【JAVA】常用加解密算法总结及JAVA实现【BASE64,MD5,SHA,DES,3DES,AES,RSA】