自己写的java实现的多路搜索树 B-Tree
最近需要写范围查询的功能,最简单的应该是B+树吧,在了解B+树的时候,也看到了B-树。于是想先实现B-Tree再实现B+Tree,结果网上并没有找到B-Tree(多路搜索树),于是自己用java实现了一个,经过自己设计了很多测试用例,用Junit(临时学的)测试可用。在这里贴出来,希望能给初学者一点参考,也希望能有高人指点可以改进的地方,欢迎讨论批评指点!自己之前一直在做工程,这是一年多来首次写数据结构,自己还很弱,欢迎大家批评指正!!!
本B-Tree理论部分参考博客:http://blog.csdn.net/v_JULY_v/article/details/6530142/
以下直接贴代码!
1 package com.test1; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.util.ArrayList; 7 import java.util.List; 8 import java.util.NoSuchElementException; 9 10 /** 11 * Created by MSI on 2016/1/5. 12 * 该程序参考文章:http://blog.csdn.net/v_JULY_v/article/details/6530142/ 13 */ 14 public class BsubTree<T extends Comparable<T>> { 15 16 private static BsubTree<Integer> tree = new BsubTree<Integer>(); 17 private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 18 19 int m = 4; //此B-树的阶数。关键字数等于阶数-1。m至少为2,m必须大于等于2。 20 int n; //n是关键字最小个数 21 public BTNode root; 22 23 public BsubTree(){ 24 root = new BTNode(null,null); 25 if(m>=2){ 26 if(m%2==0){ 27 n = m/2-1; 28 }else { 29 n = m/2; 30 } 31 }else { 32 System.out.println("error"); 33 System.exit(0); 34 } 35 } 36 37 public BsubTree(BTNode root){ 38 this.root = root; 39 if(m%2==0){ 40 n = m/2; 41 }else { 42 n = m/2+1; 43 } 44 } 45 46 public BTNode findNode(T information){ 47 return findNode(information, root); 48 } //isMember应该返回插入点 49 50 private BTNode findNode(T info, BTNode node){ //不论是否找到都返回一个node。 51 BTNode member = null; 52 53 if(node.informations.size()==0){//这种情况存在,只有root可能 54 member = node; 55 //System.out.println("error"); 56 }else { 57 if(info.compareTo(node.informations.get(node.informations.size()-1))>0) { //info比节点最大的还大,则直接进入最右分支 58 if(node.ptr.size()>0){//有孩子的情况,进入范围中的子节点 59 member = findNode(info, node.ptr.get(node.informations.size())); 60 }else {//没有子节点,直接返回node 61 member = node; 62 } 63 }else {//没有判断没有子节点的情况,上一个if中判断了,这一个else中就忘了,怒 64 if(node.ptr.size()>0){//有子节点 65 if(info.compareTo(node.informations.get(0))<0){ 66 member = findNode(info, node.ptr.get(0)); 67 }else { 68 for(int i = 0;i<node.informations.size();++i){ 69 if(info.compareTo(node.informations.get(i))==0){ 70 member = node; 71 break; 72 }else if(info.compareTo(node.informations.get(i))>0&&info.compareTo(node.informations.get(i+1))<0){ //只要不是最右,info比之大的,进入它的孩子节点 73 member = findNode(info, node.ptr.get(i+1)); 74 break; 75 } 76 } 77 } 78 }else {//没有子节点 79 member = node; 80 } 81 } 82 } 83 return member; 84 } 85 86 public void insert(T info){ 87 BTNode temp = findNode(info); 88 if(temp.informations.size()!=0){ 89 for(T i:temp.informations){ 90 if(i.compareTo(info)==0){ 91 System.out.println("已存在所插入的值。"); 92 return; 93 } 94 } 95 } 96 insert(info,temp,temp.parent); 97 return; 98 } 99 100 private void insert(T info,BTNode node,BTNode parent){//插入一定是在叶子节点 101 if(node == null){//insert中的node为空应该只有一种情况,node=root 102 if(parent == null){ 103 root = new BTNode(info,parent); 104 }else { 105 System.out.println("不应该出现的情况,请检查。"); 106 //node = new BTNode(info,parent); 107 } 108 }else{ 109 if(node.informations.size()==0){ 110 //System.out.println("这种情况应该不存在,请检查代码");//现在存在这种情况啦 111 node.informations.add(info); 112 }else if(node.informations.size()>0 && node.informations.size()<m-1){ 113 if(info.compareTo(node.informations.get(node.informations.size() - 1))>0){//info比node最右边最大的值还大,则直接插入 114 node.informations.add(info); 115 }else { 116 for (int i = 0; i < node.informations.size(); ++i) { 117 if (info.compareTo(node.informations.get(i)) < 0) { 118 node.informations.add(i, info); 119 break; 120 } 121 } 122 } 123 }else if(node.informations.size()==m-1){//需要分裂 124 if(info.compareTo(node.informations.get(node.informations.size()-1))>0){//info比node最右边最大的值还大,则直接插入 125 node.informations.add(info); 126 }else { 127 for(int i = 0;i<node.informations.size();++i){ 128 if(info.compareTo(node.informations.get(i))<0){ 129 node.informations.add(i,info); 130 break; 131 } 132 } 133 } 134 135 split(node); 136 }else { 137 System.out.println("node 的size大于等于m-1,不应该出现,请检查代码"); 138 } 139 } 140 } 141 142 public void delete(T info){ 143 BTNode temp = findNode(info,root); 144 if(temp.informations.size()==0){ 145 System.out.println("根节点为空!"); 146 return; 147 } 148 for(T i:temp.informations){ 149 if(info.compareTo(i)==0){ 150 delete(info,temp); 151 break; 152 }else if(temp.informations.indexOf(i)==temp.informations.size()-1){//循环到最后一个值了,仍到这里,说明不存在要删除的值! 153 System.out.println("不存在要删除的值!"); 154 } 155 } 156 } 157 158 private void delete(T info,BTNode node)throws NoSuchElementException { 159 if (node == null) { //其实到这里,就一定存在要删除的值了。 160 throw new NoSuchElementException(); 161 }else { 162 int i; 163 for(i=0;i<node.informations.size();i++){ 164 if(info.compareTo(node.informations.get(i))==0){ 165 node.informations.remove(i); //删除关键字,其实要是索引向文件,也应该删除文件。 166 break; 167 } 168 } 169 if(node.ptr.size()>0){//删除一个非叶子节点的关键字后,如果有孩子,则判断孩子的孩子,如果孩子有孩子,则将右孩子的孩子最深左孩子的第一个值赋给删除关键字的节点 170 //每一个关键字,一定有两个孩子 171 if(node.ptr.get(i+1).ptr.size()==0){//孩子没有孩子的时候,只将孩子的最左关键字上升。 172 node.informations.add(i,node.ptr.get(i+1).informations.get(0)); 173 node.ptr.get(i+1).informations.remove(0); 174 if(node.ptr.get(i+1).informations.size()<n){ 175 dManageNode(node.ptr.get(i+1)); 176 } 177 }else {//孩子有孩子的时候,则将右孩子的孩子最深左孩子的第一个值赋给删除关键字的节点 178 pullRLeftNode(node,i,node.ptr.get(i+1),i); 179 } 180 181 }else {//如果没有孩子,要判断该节点关键字数量是否大于最小值 182 if(node.informations.size()>=n){//大于等于就没事,不用动 183 return; 184 }else {//叶子节点中关键字数小于n,需要继续判断兄弟节点是否饱满 185 dManageNode(node); 186 } 187 } 188 } 189 } 190 191 public String perOrder(BTNode node){ 192 String result = ""; 193 if(node.ptr.size()>0){ 194 int i = 0; 195 for (BTNode n:node.ptr){ 196 result += perOrder(n); 197 if(i<node.informations.size()){ 198 result += node.informations.get(i).toString()+","; 199 ++i; 200 } 201 } 202 }else {//叶子节点 203 if(node.informations.size()>0){ 204 for (T t:node.informations){ 205 result += t.toString()+","; 206 } 207 }else {//叶子节点没有空值的时候,除非是根节点,根节点为空值的时候,说句话意思意思 208 result += "B-树为空!"; 209 } 210 } 211 return result; 212 } 213 214 public void split(BTNode node){//进到这里的node都是m个关键字,需要提出m/2 215 if(node == null){ 216 System.out.println("error"); 217 }else { 218 if(node.informations.size()!=m){ 219 System.out.println("error"); 220 }else { 221 if(node.parent == null){//node是root时 222 T temp = node.informations.get(n);//这里正好 223 root = new BTNode(temp,null); 224 node.informations.remove(n);//加进去了就要删掉! 225 root.ptr.add(node); 226 node.parent=root; 227 splitNewNode(node,n,root); 228 }else {//一个非根节点 229 T temp = node.informations.get(n); 230 node.parent.informations.add(node.parent.ptr.indexOf(node),temp); 231 node.informations.remove(n); 232 splitNewNode(node,n,node.parent); 233 if (node.parent.informations.size()>=m){ 234 split(node.parent); 235 } 236 } 237 } 238 } 239 } 240 241 public void splitNewNode(BTNode node,int n,BTNode parent){ 242 BTNode newnode = new BTNode(node.informations.get(n),node.parent); 243 244 newnode.informations.addAll(node.informations.subList(n+1,node.informations.size())); 245 246 node.informations.removeAll(node.informations.subList(n,node.informations.size())); 247 //newnode.parent=node.parent;//新增节点的父节点 248 node.parent.ptr.add(node.parent.ptr.indexOf(node)+1,newnode); //新增节点加到父节点上 249 if(node.ptr.size()>0){ //处理新增节点的孩子 250 newnode.ptr.addAll(node.ptr.subList(n+1,node.ptr.size())); 251 node.ptr.removeAll(node.ptr.subList(n+1,node.ptr.size())); 252 for (BTNode bn:newnode.ptr){ //子节点移到了新节点上,但是子节点的父节点没有处理!!!T_T 253 bn.parent = newnode; 254 } 255 } 256 257 } 258 259 public void combine(BTNode lnode,BTNode rnode){ 260 if(lnode.informations.size()<n){ 261 lnode.informations.add(lnode.parent.informations.get(lnode.parent.ptr.indexOf(lnode))); 262 lnode.parent.informations.remove(lnode.parent.ptr.indexOf(lnode)); 263 }else if(rnode.informations.size()<n){ 264 rnode.informations.add(0,rnode.parent.informations.get(lnode.parent.ptr.indexOf(lnode))); 265 rnode.parent.informations.remove(lnode.parent.ptr.indexOf(lnode)); 266 }else { 267 System.out.println("error"); 268 } 269 270 lnode.informations.addAll(rnode.informations); 271 lnode.ptr.addAll(rnode.ptr); 272 for (BTNode n:rnode.ptr){ 273 n.parent=lnode; 274 } 275 lnode.parent.ptr.remove(lnode.parent.ptr.indexOf(lnode)+1); 276 if(lnode.parent.parent==null&&lnode.parent.informations.size()==0){//父节点是根节点 277 lnode.parent = null; //lnode为新的根节点 278 root = lnode; 279 return; 280 } 281 if(lnode.parent.informations.size()<n){ 282 dManageNode(lnode.parent); 283 } 284 } 285 286 public void lrotate(BTNode lnode,BTNode rnode){ 287 lnode.informations.add(lnode.parent.informations.get(lnode.parent.ptr.indexOf(lnode))); 288 lnode.parent.informations.remove(lnode.parent.ptr.indexOf(lnode)); 289 lnode.parent.informations.add(lnode.parent.ptr.indexOf(lnode),rnode.informations.get(0)); 290 rnode.informations.remove(0); 291 if(rnode.ptr.size()>0){//要判断叶子节点没有孩子! 292 lnode.ptr.add(rnode.ptr.get(0)); 293 rnode.ptr.remove(0); 294 lnode.ptr.get(lnode.ptr.size()-1).parent=lnode; 295 } 296 297 } 298 299 public void rrotate(BTNode lnode,BTNode rnode){ 300 rnode.informations.add(rnode.parent.informations.get(lnode.parent.ptr.indexOf(lnode))); 301 rnode.parent.informations.remove(lnode.parent.ptr.indexOf(lnode)); 302 rnode.parent.informations.add(lnode.parent.ptr.indexOf(lnode),lnode.informations.get(lnode.informations.size()-1)); 303 lnode.informations.remove(lnode.informations.size()-1); 304 if(lnode.ptr.size()>0){ 305 rnode.ptr.add(0,lnode.ptr.get(lnode.ptr.size()-1)); 306 lnode.ptr.remove(lnode.ptr.size()-1); 307 rnode.ptr.get(0).parent=rnode; 308 } 309 } 310 311 public void dManageNode(BTNode node){//叶子节点中关键字数小于n,需要继续判断兄弟节点是否饱满,是旋转还是合并 312 if(node.parent==null){ 313 return; 314 }else { 315 int x = node.parent.ptr.indexOf(node); 316 if(x==0){//被删除关键字所在节点,是父节点最左边的节点时,判断右兄弟,而且肯定有右兄弟 317 if(node.parent.ptr.get(x+1).informations.size()==n){//刚脱贫,需要合并 318 combine(node,node.parent.ptr.get(x+1)); 319 }else if(node.parent.ptr.get(x+1).informations.size()>n){//关键字数大于最小值,丰满 320 lrotate(node, node.parent.ptr.get(x + 1)); 321 }else { 322 System.out.println("error"); 323 } 324 }else if(x==node.parent.ptr.size()-1){//是父节点最右边的节点时,判断左兄弟 325 if(node.parent.ptr.get(x-1).informations.size()==n){//左兄弟刚脱贫,需要合并 326 combine(node.parent.ptr.get(x-1),node); 327 }else if(node.parent.ptr.get(x-1).informations.size()>n){//关键字数大于最小值,丰满 328 rrotate(node.parent.ptr.get(x-1),node); 329 }else { 330 System.out.println("error"); 331 } 332 }else {//node在父节点的子节点的中间,需要先判断左兄弟,再判断右兄弟。靠,感觉判断兄弟是否饱满,还是应该写一个函数,也许可以传递两个值 333 //先跟饱满的借,除非两个兄弟都刚脱贫。 334 if(node.parent.ptr.get(x-1).informations.size()>n){//左兄弟丰满 335 rrotate(node.parent.ptr.get(x - 1),node); 336 }else if(node.parent.ptr.get(x+1).informations.size()>n){//右兄弟丰满 337 lrotate(node, node.parent.ptr.get(x + 1)); 338 }else{//左右兄弟都刚脱贫,需要合并 339 combine(node.parent.ptr.get(x-1),node); 340 } 341 } 342 } 343 344 } 345 346 public void pullRLeftNode(BTNode donode,int j,BTNode node,int i){//节点删除关键字后,如果该节点有孩子,则孩子需要贡献关键字,由于孩子减少了关键字还需要向下借,一直递归到叶子。 347 348 if(node.ptr.get(0).ptr.size()>0){ 349 pullRLeftNode(donode,j,node.ptr.get(0),0); 350 }else { 351 donode.informations.add(j,node.ptr.get(0).informations.get(0)); 352 node.ptr.get(0).informations.remove(0); 353 if(node.ptr.get(0).informations.size()<n){ 354 dManageNode(node.ptr.get(0)); 355 } 356 } 357 } 358 359 class BTNode{ 360 BTNode parent; //父节点 361 List<T> informations = new ArrayList<T>(); //关键字的信息 362 List<BTNode> ptr = new ArrayList<BTNode>(); //分支 363 364 public BTNode(T information,BTNode parent){ 365 if(information != null){ 366 informations.add(information); 367 this.parent = parent; 368 }else { 369 this.parent = null; 370 } 371 } 372 373 boolean isLeaf(){ 374 return (ptr.size()==0); 375 } 376 377 boolean isNode(){ 378 return (ptr.size()!=0); 379 } 380 381 int infoLength(){ 382 return informations.size(); 383 } 384 385 int ptrLength(){ 386 return ptr.size(); 387 } 388 389 } 390 391 private static String stringInput(String inputRequest)throws IOException{ 392 System.out.println(inputRequest); 393 return reader.readLine(); 394 } 395 396 public static void main(String[] args)throws IOException { 397 System.out.println("test B - balanced tree operations"); 398 System.out.println("*****************************"); 399 400 String input; 401 Integer value; 402 403 do { 404 input = stringInput("please select: [i]nsert, [d]elete, [s]how, [e]xit"); 405 switch (input.charAt(0)) { 406 case 'i': 407 value = Integer.parseInt(stringInput("insert: "), 10); 408 tree.insert(value); 409 break; 410 case 'd': 411 value = Integer.parseInt(stringInput("delete: "), 10); 412 tree.delete(value); 413 break; 414 case 's': 415 System.out.println(tree.perOrder(tree.root)); 416 break; 417 // case 'h': 418 // System.out.println(tree.getHeight()); 419 } 420 } while ((input.charAt(0) != 'e')); 421 } 422 }