感觉上,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;
}
}
}
}