感觉上,b树的插入及删除操作都不如RB树复杂。当年插红黑树的各种操作解释文章都不下几十篇了,数据结构及算法的调试正确运行是一个大问题,因为错误往往出现在细微处。
package BTree; public class indexUnit { public float indexNo; public Object indexValue=new Object(); }
package BTree; import java.util.ArrayList; public class TreeUnit { public TreeUnit _parent=null; public ArrayList<indexUnit> keys=new ArrayList<indexUnit>(); public ArrayList<TreeUnit> childNodes=new ArrayList<TreeUnit>(); }
package BTree; /** * Created with IntelliJ IDEA. * User: Administrator * Date: 13-8-19 * Time: 上午10:21 * To change this template use File | Settings | File Templates. */ public class BtreeGen { public TreeUnit _btreeRootNode=new TreeUnit(); /* * 请注意,这个是最多容纳的子树的阶,b树的阶应该是最多的子树数量, * 根据b树的性质,最大主键数量+1=最多子树数量。 * 并且,任何时刻,主键的数量比下属子树数量少1. * */ public int _m=6; private int _min=3; public int totalKeys=1; /** * 确定该b树的最大子节点数。 * */ public BtreeGen(int m){ _m=m; _min=(int)Math.ceil((double)m/2); } public boolean insert(float indexNO,Object indexValue){ indexUnit iunit=new indexUnit(); iunit.indexNo=indexNO; iunit.indexValue=indexValue; TreeUnit needInsertLeaf=recursion_insert_search_leaf_node(indexNO, _btreeRootNode); if(needInsertLeaf==null){ //-- System.out.println("【警告】找不到需要插入的叶节点!"); return false; } else{ System.out.println("【提示】需要插入的叶节点为:"); for(indexUnit iiUnit:needInsertLeaf.keys){ //-- System.out.print(" "+iiUnit.indexNo+" "); } } to_insert(indexNO, indexValue, needInsertLeaf); return true; } private TreeUnit recursion_insert_search_leaf_node(float indexNO,TreeUnit currentUnit){ if(currentUnit==null){ return null; } //--假如有下属节点,那么就必须跳到下一个点。 if(currentUnit.childNodes.size()>0){ int childLoc=0; int cindex=0; for(indexUnit iUnit:currentUnit.keys){ if(iUnit.indexNo<indexNO){ childLoc=cindex+1; } if(iUnit.indexNo==indexNO){ //--已经包含该节点了?那么就返回空,不要再插了。 return null; } cindex++; } TreeUnit childTree=currentUnit.childNodes.get(childLoc); return recursion_insert_search_leaf_node(indexNO, childTree); } else{ //--没有下属节点,那么就认定是这个叶节点了。 return currentUnit; } } /* * 主键的插入。 * */ private void to_insert(float indexNO,Object value,TreeUnit currentUnit){ int insertLoc=0; for(indexUnit iUnit:currentUnit.keys){ if(iUnit.indexNo>indexNO){ break; } insertLoc++; } indexUnit insertedUnit=new indexUnit(); insertedUnit.indexNo=indexNO; insertedUnit.indexValue=value; currentUnit.keys.add(insertLoc, insertedUnit); if(currentUnit.keys.size()>_m-1){ recursion_division(currentUnit); } else{ return; } } private void recursion_division(TreeUnit currentUnit){ if(currentUnit==null){ return; } if(currentUnit.keys.size()<=_m-1){ return; } if(currentUnit._parent==null){ System.out.println("没有父节点。"); TreeUnit leftTree=new TreeUnit(); TreeUnit rightTree=new TreeUnit(); leftTree._parent=currentUnit; rightTree._parent=currentUnit; indexUnit keyUnit=currentUnit.keys.get(_min-1); int cindex=0; for (indexUnit tmp:currentUnit.keys) { if(cindex<_min-1){ leftTree.keys.add(tmp); } else if(cindex>=_min){ rightTree.keys.add(tmp); } cindex++; } int theSize=currentUnit.childNodes.size(); currentUnit.keys.clear(); currentUnit.keys.add(keyUnit); if(currentUnit.childNodes.size()>0){ //-- for(int ii=0;ii<theSize;ii++){ if(ii<=_min-1){ currentUnit.childNodes.get(ii)._parent=leftTree; leftTree.childNodes.add(currentUnit.childNodes.get(ii)); } else{ currentUnit.childNodes.get(ii)._parent=rightTree; rightTree.childNodes.add(currentUnit.childNodes.get(ii)); } } } currentUnit.childNodes.clear(); currentUnit.childNodes.add(leftTree); currentUnit.childNodes.add(rightTree); return; } System.out.println("父节点不为空。"); //--分裂成为了旧节点及新节点两个节点。 indexUnit keyUnit=currentUnit.keys.get(_min-1); TreeUnit newTreeUnit=new TreeUnit(); newTreeUnit._parent=currentUnit._parent; int cindex=0; for (indexUnit tmp:currentUnit.keys) { if(cindex>=_min){ newTreeUnit.keys.add(tmp); } cindex++; } int theSize=currentUnit.keys.size(); for(int i2=theSize-1;i2>=_min-1;i2--){ currentUnit.keys.remove(i2); } cindex=0; theSize=currentUnit.childNodes.size(); for(int ii4=theSize-1;ii4>=_min;ii4--){ TreeUnit tmp2=currentUnit.childNodes.get(ii4); tmp2._parent=newTreeUnit; if(newTreeUnit.childNodes.size()<=0){ newTreeUnit.childNodes.add(tmp2); } else{ newTreeUnit.childNodes.add(0,tmp2); } } for(int ii3=theSize-1;ii3>=_min;ii3--){ currentUnit.childNodes.remove(ii3); } int insertPLoc=0; cindex=0; for(indexUnit iUnit:currentUnit._parent.keys){ if(iUnit.indexNo<keyUnit.indexNo){ insertPLoc=cindex+1; } cindex++; } currentUnit._parent.keys.add(insertPLoc, keyUnit); currentUnit._parent.childNodes.add(insertPLoc+1, newTreeUnit); //--给父节点添加相应子节点。 if(currentUnit._parent.keys.size()>_m-1){ recursion_division(currentUnit._parent); } return; } public indexUnit search(float indexNO){ _searchResultUnit=null; recursion_search(_btreeRootNode,indexNO); return _searchResultUnit; } private indexUnit _searchResultUnit=null; private void recursion_search(TreeUnit currentUnit, float indexNO){ if(currentUnit==null){ _searchResultUnit=null; return; } for(indexUnit f1:currentUnit.keys){ if(f1.indexNo==indexNO){ _searchResultUnit=f1; return; } } //--假如上面都找不到,并且该节点下面没有子树了,那么就表示没有这个东西。 if(currentUnit.childNodes.size()<=0){ return; } int childTreeIndex=0; int ccIndex=0; for(indexUnit f2:currentUnit.keys){ if(f2.indexNo<indexNO){ childTreeIndex=ccIndex+1; } ccIndex++; } TreeUnit childTreeUnit=currentUnit.childNodes.get(childTreeIndex); recursion_search(childTreeUnit, indexNO); } private TreeUnit _result_treeUnit=null; /** * 获取indexNO所在的节点。 * */ public TreeUnit getSearchNode(float indexNO){ _result_treeUnit=null; recursion_search_node(_btreeRootNode, indexNO); return _result_treeUnit; } /** *搜查indexNO所在的节点。 * */ private void recursion_search_node(TreeUnit treeUnit,float indexNO){ if(treeUnit==null){ return; } int childChosenIndex=0; int cindex=0; for(indexUnit iTMP:treeUnit.keys){ if(indexNO>iTMP.indexNo){ childChosenIndex=cindex+1; } if(iTMP.indexNo==indexNO){ _result_treeUnit=treeUnit; return; } cindex++; } if(treeUnit.childNodes.size()<=0){ return; } //--假如有下面并且当前没包含相关主键,那么就搜索下面子节点的。 TreeUnit childTreeUnit=treeUnit.childNodes.get(childChosenIndex); recursion_search_node(childTreeUnit, indexNO); } public int[] getRoute(float indexNO){ return null; } public boolean delete(float indexNO){ TreeUnit needDelNode=getSearchNode(indexNO); if(needDelNode==null){ return false; } return deleteNode(needDelNode, indexNO); } /** * 删除相关节点。 * 删除有几种情况: * 1、假如是叶节点,并且删除以后符合b树的性质,譬如:m=6---最大主键数量为5,最少主键数量为2,叶节点删除主键后,假如关键字数量》=2,那么就完成删除操作, * 否则: * 1、看看有没有左右节点,假如左右节点有空余的关键字,那么就借用相应的关键字, * 假如左右节点的关键字数量都是2,没有空余的,那么只能想父节点借用,并且可能会发生合并操作。 * * */ public boolean deleteNode(TreeUnit needDelNode,float indexNO){ //System.out.println("需要删除的节点为:"+needDelNode.keys.get(0).indexNo); //--找到当前关键字的在节点里面的位置。 int indexLoc=-1; int cindex=0; for(indexUnit iUnit:needDelNode.keys){ if(iUnit.indexNo==indexNO){ indexLoc=cindex; } cindex++; } if(indexLoc==-1){ return false; } TreeUnit leftChildNode=null; TreeUnit rightChildNode=null; if(needDelNode.childNodes.size()>0){ leftChildNode=needDelNode.childNodes.get(indexLoc); rightChildNode=needDelNode.childNodes.get(indexLoc+1); } /** * 假如关键字所在节点为根节点,并且没有任何下属节点,那么就直接删除好了。 * */ if(needDelNode._parent==null&&(needDelNode.childNodes==null||needDelNode.childNodes.size()<=0)){ needDelNode.keys.remove(indexLoc); return true; } /** * 假如关键字包含子节点,那么就进行相关调整,需要递归调整到叶子节点为止。请注意,现在只是调整调换关键字的数值,并没有删除任何关键字,所以 * b树的结构没有破坏。 * */ /** * a.如果x的左孩子节点存在,x->child[i],并且x->child[i]的节点关键字个数至少是n个, * 则找到 child[i]中最大的关键字max替换x中删除的关键字,继续递归child[i]删除关键字max。 * 这一段话引用自网上,但是我想说,假如该节点不是叶子节点,那么对于该节点的某一个关键字keys[i], * 其左子结点及右子节点child[i]与child[i+1]必然存在----这是b树的基本性质。 * */ if(needDelNode.childNodes!=null&&needDelNode.childNodes.size()>0){ //假如左节点有空余的关键字可以使用(解释:对于6阶b树--最多6个子树,每个节点最少有3棵子树,最多5个节点,最少2个节点,有空余关键字是指关键字数量大于或等于3个) if(leftChildNode!=null&&leftChildNode.keys.size()>=_min){ int leftLastIndex=leftChildNode.keys.size()-1; needDelNode.keys.remove(indexLoc); needDelNode.keys.add(indexLoc,leftChildNode.keys.get(leftLastIndex)); float indexNO1=needDelNode.keys.get(indexLoc).indexNo; //--递归执行 return deleteNode(leftChildNode, indexNO1); } //--假如右侧节点有关键字空余 else if(rightChildNode.keys.size()>=_min){ int rightLastIndex=0; needDelNode.keys.remove(indexLoc); needDelNode.keys.add(indexLoc, rightChildNode.keys.get(0)); return deleteNode(rightChildNode, rightChildNode.keys.get(0).indexNo); } else{ //--假如左右子节点都没有空余节点了,那么只能合并了。 leftChildNode.keys.add(needDelNode.keys.get(indexLoc)); for(indexUnit iUnit:rightChildNode.keys){ leftChildNode.keys.add(iUnit); } for(TreeUnit item1:rightChildNode.childNodes){ leftChildNode.childNodes.add(item1); } needDelNode.keys.remove(indexLoc); needDelNode.childNodes.remove(indexLoc+1); //--检查父节点是否符合性质,是否需要回溯合并节点。 /** * 请注意:这个地方是造成回溯合并的主要情形。 * */ recursion_checkCombination(needDelNode); return deleteNode(leftChildNode, indexNO); } } /** * * 假如是叶子节点,那么就执行相关操作。 * * */ else if(needDelNode.childNodes==null||needDelNode.childNodes.size()<=0){ if(needDelNode.keys.size()>=_min){ needDelNode.keys.remove(indexLoc); return true; } //--- TreeUnit leftBrother=null; TreeUnit rightBrother=null; TreeUnit parentNode=needDelNode._parent; int childIndexLoc=parentNode.childNodes.indexOf(needDelNode); int keyIndexLoc=-1; if(childIndexLoc==0){ rightBrother=parentNode.childNodes.get(1); } else if(childIndexLoc==parentNode.childNodes.size()-1){ leftBrother=parentNode.childNodes.get(childIndexLoc-1); } else{ leftBrother=parentNode.childNodes.get(childIndexLoc-1); rightBrother=parentNode.childNodes.get(childIndexLoc+1); } //--假如左侧兄弟存在并且有多余节点那么就借用了。 if(leftBrother!=null&&leftBrother.keys.size()>=_min){ keyIndexLoc=childIndexLoc-1; needDelNode.keys.add(0,parentNode.keys.get(keyIndexLoc)); parentNode.keys.remove(keyIndexLoc); parentNode.keys.add(keyIndexLoc,leftBrother.keys.get(leftBrother.keys.size()-1)); leftBrother.keys.remove(leftBrother.keys.size()-1); return deleteNode(needDelNode, indexNO); } //右侧兄弟有多余的。 else if(rightBrother!=null&&rightBrother.keys.size()>=_min){ keyIndexLoc=childIndexLoc; needDelNode.keys.add(parentNode.keys.get(keyIndexLoc)); parentNode.keys.add(keyIndexLoc,rightBrother.keys.get(0)); parentNode.keys.remove(keyIndexLoc+1); rightBrother.keys.remove(0);///------------- return deleteNode(needDelNode, indexNO); } //--两个兄弟都没有多余的,那么就合并好了。 else{ if(leftBrother!=null){ //leftBrother.keys.add(parentNode.keys.get(keyIndexLoc)); keyIndexLoc=childIndexLoc-1; leftBrother.keys.add(parentNode.keys.get(keyIndexLoc)); for(indexUnit iUnit:needDelNode.keys){ leftBrother.keys.add(iUnit); } parentNode.keys.remove(keyIndexLoc); parentNode.childNodes.remove(keyIndexLoc+1); recursion_checkCombination(parentNode); deleteNode(leftBrother, indexNO); return true; } else if(rightBrother!=null){ //needDelNode.keys.remove(indexLoc); keyIndexLoc=childIndexLoc; needDelNode.keys.add(parentNode.keys.get(keyIndexLoc)); for(indexUnit iUnit:rightBrother.keys){ needDelNode.keys.add(iUnit); } parentNode.keys.remove(keyIndexLoc); parentNode.childNodes.remove(keyIndexLoc+1); recursion_checkCombination(parentNode); deleteNode(needDelNode, indexNO); return true; } else{ return false; } } } return true; } private void recursion_checkCombination(TreeUnit currentUnit){ if(currentUnit==null){ return; } if(currentUnit._parent==null&¤tUnit.childNodes.size()<=1){ //假如当前节点为根节点,并且没有关键字,那么根节点丢弃,采用新的根节点。 _btreeRootNode=currentUnit.childNodes.get(0); _btreeRootNode._parent=null; return; } //假如当前节点为根节点,并且关键字为一,那么就不管了,符合性质。 if(currentUnit._parent==null){ return; } if(currentUnit.keys.size()>=_min-1){ return; } //假如不为根节点,并且节点的关键字小于_min-1,那么必须再回溯递归合并。 /** * 分几种情况,假如左侧子节点有空余关键字,那么从左侧节点借,假如右侧节点有空余关键字,那么从右侧节点借, * 否则两边都没有只能继续递归合并了。 * */ TreeUnit parentNode=currentUnit._parent; int indexLOC=currentUnit._parent.childNodes.indexOf(currentUnit); int keyIndexLOC=-1; int childIndexLOC=indexLOC; TreeUnit leftBrother=null; TreeUnit rightBrother=null; if(parentNode.childNodes.size()==2){ if(childIndexLOC==0){ rightBrother=parentNode.childNodes.get(1); } else{ leftBrother=parentNode.childNodes.get(0); } } else if(parentNode.childNodes.size()>=3){ if(childIndexLOC==0){ rightBrother=parentNode.childNodes.get(1); } else if(childIndexLOC==parentNode.childNodes.size()-1){ leftBrother=parentNode.childNodes.get(childIndexLOC-1); } else{ leftBrother=parentNode.childNodes.get(childIndexLOC-1); rightBrother=parentNode.childNodes.get(childIndexLOC+1); } } //--左边兄弟节点有余钱,那么就借。 if(leftBrother!=null&&leftBrother.keys.size()>=_min){ keyIndexLOC=childIndexLOC-1; currentUnit.keys.add(0, parentNode.keys.get(keyIndexLOC)); currentUnit.childNodes.add(0,leftBrother.childNodes.get(leftBrother.childNodes.size()-1)); parentNode.keys.remove(keyIndexLOC); parentNode.keys.add(keyIndexLOC,leftBrother.keys.get(leftBrother.keys.size()-1)); leftBrother.keys.remove(leftBrother.keys.size()-1); leftBrother.childNodes.remove(leftBrother.childNodes.size()-1); return; } //--右边兄弟有余钱,那么就借 else if(rightBrother!=null&&rightBrother.keys.size()>=_min){ keyIndexLOC=childIndexLOC; currentUnit.keys.add(parentNode.keys.get(keyIndexLOC)); currentUnit.childNodes.add(rightBrother.childNodes.get(0)); parentNode.keys.remove(keyIndexLOC); parentNode.keys.add(rightBrother.keys.get(0)); rightBrother.keys.remove(0); rightBrother.childNodes.remove(0); return; } //--大家都没得借,那么就只能递归合并了。 else{ //--有左侧兄弟 if(leftBrother!=null){ keyIndexLOC=childIndexLOC-1; leftBrother.keys.add(parentNode.keys.get(keyIndexLOC)); for(indexUnit iUnit:currentUnit.keys){ leftBrother.keys.add(iUnit); } for(TreeUnit tUnit:currentUnit.childNodes){ leftBrother.childNodes.add(tUnit); tUnit._parent=leftBrother; } parentNode.keys.remove(keyIndexLOC); parentNode.childNodes.remove(keyIndexLOC+1); recursion_checkCombination(parentNode); return; } else{ //--有右侧兄弟 keyIndexLOC=childIndexLOC; currentUnit.keys.add(parentNode.keys.get(keyIndexLOC)); for(indexUnit iUnit:rightBrother.keys){ currentUnit.keys.add(iUnit); } for(TreeUnit iUnit:rightBrother.childNodes){ currentUnit.childNodes.add(iUnit); iUnit._parent=currentUnit; } parentNode.keys.remove(keyIndexLOC); parentNode.childNodes.remove(keyIndexLOC+1); recursion_checkCombination(parentNode); return; } } } }