编程内功修炼之数据结构—BTree(二)实现BTree插入、查询、删除操作
1 package edu.algorithms.btree; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * BTree类 8 * 9 * @author lingfeng 10 * 11 */ 12 public class BTree { 13 14 /**BTree 基础参数信息配置 15 最小度数 t=2时,称作2-3-4数,表示只能存在2、3、4子女数**/ 16 private int t = 2; 17 18 /**非根节点最小关键字数**/ 19 private int minKeys = t-1; 20 21 /**内节点最大关键字数**/ 22 private int maxKeys = 2*t - 1; 23 24 /**树根节点**/ 25 private BTreeNode root; 26 27 /**构造函数**/ 28 public BTree(){ 29 //初始化 30 root = new BTreeNode(); 31 root.setLeaf(true); 32 } 33 /**构造函数 t 最小度数**/ 34 public BTree(int t){ 35 this(); 36 this.t = t; 37 minKeys = t - 1; 38 maxKeys = 2*t - 1; 39 } 40 41 /**插入元素 42 * 43 * 1.元素存在,插入元素 44 * 2.元素不存在,插入元素 45 * 46 * **/ 47 public void insertNode(Integer key){ 48 if(root.size() == maxKeys){ //根节点已满 49 //进行根节点分割 50 BTreeNode tmpNode = new BTreeNode(); 51 tmpNode.setLeaf(false); 52 tmpNode.getChildrens().add(root); 53 btreeSplitChild(tmpNode, 0, root); 54 root = tmpNode; 55 } 56 insertRootNotFull(root, key); 57 } 58 59 public void insertRootNotFull(BTreeNode node, int key){ 60 61 //如果该节点为叶子节点 62 if(node.isLeaf()){ 63 //寻找当前节点所有关键字 64 ResultSearch result = divideSearch(node.getKeys(), key); 65 //查询结果:成功 返回已经存在关键字位置 66 if(result.result == true){ 67 68 }else{ //查询结果:失败 返回插入位置 69 node.insertKey(key, result.index); 70 } 71 72 }else{ 73 //寻找当前节点所有关键字 74 ResultSearch result = divideSearch(node.getKeys(), key); 75 //查询结果:成功 返回已经存在关键字位置 76 if(result.result == true){ 77 78 }else{ //查询结果:失败 返回插入子女节点的位置 79 //判断子女状态 80 BTreeNode childNode = node.childAt(result.index); 81 if(node.childAt(result.index).size() == (2*t - 1)){ 82 btreeSplitChild(node, result.index, childNode); 83 if(key > node.keyAt(result.index)){ //判断key落在 前半部分 或 后半部分 84 childNode = node.childAt(result.index + 1); 85 } 86 } 87 insertRootNotFull(childNode, key); 88 } 89 } 90 } 91 /**查询元素 (BTree查询) 92 * 93 * 1.先查询节点的关键字列表 94 * 2.失败则,查询子女节点;成功则,返回值 95 * 3.递归搜索 96 * 97 * **/ 98 public Integer search(BTreeNode node,Integer key){ 99 List<Integer> keys_tmp = node.getKeys(); 100 ResultSearch result = divideSearch(keys_tmp, key); 101 if(result.result){ 102 return result.index; 103 }else{ 104 return search(node.childAt(result.index), key); 105 } 106 } 107 /**二分查询 108 * 参数 元素列表、所需要查询元素的值 109 * 110 * **/ 111 public ResultSearch divideSearch(List<Integer> elements, int key){ 112 113 int start = 0; //扫描起始位置 114 int end = 0; //扫描结束位置 115 116 end = elements.size() - 1; 117 118 int mid = 0; 119 while(start<=end){ 120 mid = (start + end)/2; 121 if(elements.get(mid) == key){ //满足等值条件 122 break; 123 }else if(elements.get(mid) > key){ //在列表前半部分 124 //改变扫描结束位置 125 end = mid - 1; 126 }else if(elements.get(mid) <= key){ 127 //改变扫描开始位置 128 start = mid + 1; 129 } 130 } 131 132 boolean result = false; 133 Integer index = 0; 134 135 if(start<=end){ //二分查找成功 136 result = true; 137 index = mid; 138 }else{ //查找失败,不存在元素 139 result = false; 140 index = start; 141 } 142 143 return new ResultSearch(result,index); 144 } 145 146 /** 147 * 节点分割函数 148 * @param parentNode 被分割节点的父节点 149 * @param index 被分割节点在父节点的第index个子女索引位置 150 * @param Node 被分割节点 151 */ 152 public void btreeSplitChild(BTreeNode parentNode, int index, BTreeNode node){ 153 154 //新建一个节点,保存分割后[t+1 2t-1]数据 155 BTreeNode subNode = new BTreeNode(); 156 //必须加上 157 subNode.setLeaf(node.isLeaf()); 158 159 for(int i=1; i<=minKeys ; i++){ 160 subNode.getKeys().add(node.keyAt(t + i -1)); 161 } 162 //保存分割点值[t] 163 Integer key = node.keyAt(t - 1); 164 //删除被分割节点的[t 2t-1]数据 165 for(int i= maxKeys - 1; i>=minKeys; --i){ 166 node.getKeys().remove(i); 167 } 168 if(!node.isLeaf()){ //如果满节点不是叶节点,关键字分割后,需要将其子女进行操作 169 170 //subNode应该拥有后半部分的子女 171 for(int i=0 ; i<minKeys+1 ; ++i){ 172 subNode.getChildrens().add(node.childAt(i+t)); 173 } 174 //并删除node后半部分的子女 175 for(int i=maxKeys ; i>=minKeys+1; --i){ 176 node.getChildrens().remove(i); 177 } 178 }else{ 179 180 } 181 //将[t]数据加入父节点中去 182 parentNode.insertKey(key, index); 183 //将新节点关联到父节点index+1子女中 184 parentNode.insertChild(subNode, index+1); 185 } 186 public void delete(Integer key){ 187 delete(root, key); 188 } 189 190 public void delete(BTreeNode node, Integer key){ 191 //删除关键字时,必须保证关键字大于等于t 192 assert node.size() >=t || node == root; 193 194 //对当前节点进行二分查找 195 ResultSearch resultSearch = divideSearch(node.getKeys(), key); 196 197 //成功 198 if(resultSearch.result){ 199 200 //如果当前节点属于叶子节点,可以直接进行删除 201 if(node.isLeaf()){ 202 node.getKeys().remove(resultSearch.index.intValue()); 203 }else{ 204 //如果不是叶子节点 ,判断前于key子节点状态 205 BTreeNode leftChildNode = node.childAt(resultSearch.index); 206 if(leftChildNode.size() >= t){ 207 208 //从leftChildNode进行借值 代替当前需要删除的关键字 209 //删除当前节点关键字 210 node.getKeys().remove(resultSearch.index.intValue()); 211 node.insertKey(leftChildNode.keyAt(leftChildNode.size()-1), resultSearch.index); 212 delete(leftChildNode, leftChildNode.keyAt(leftChildNode.size()-1)); 213 }else{ 214 215 BTreeNode rightChildNode = node.childAt(resultSearch.index + 1); 216 if(rightChildNode.size() >= t){ 217 218 //从rightChildNode进行借值 代替当前需要删除的关键字 219 node.getKeys().remove(resultSearch.index.intValue()); 220 node.insertKey(rightChildNode.keyAt(0), resultSearch.index); 221 delete(rightChildNode, rightChildNode.keyAt(0)); 222 }else{ 223 224 //对于索引的左右子节点的数量都等于t-1 225 //合适进行合并 226 //1.将父节点删除 将节点右子节点删除 227 node.getKeys().remove(resultSearch.index.intValue()); 228 node.getChildrens().remove(resultSearch.index.intValue() + 1); 229 //2.将父节点添加到左子节点上 230 leftChildNode.getKeys().add(key); 231 //3.将删除的右子节点添加到左子节点上 232 for(int i=0 ; i<rightChildNode.size() ; i++){ 233 leftChildNode.getKeys().add(rightChildNode.getKeys().get(i)); 234 } 235 //如果右子节点非叶子节点,需要将其子女继承到左节点之下 236 if(!rightChildNode.isLeaf()){ 237 for(int k=0 ; k<=rightChildNode.size() ; k++){ 238 leftChildNode.getChildrens().add(rightChildNode.childAt(k)); 239 } 240 } 241 //递归删除 242 delete(leftChildNode, key); 243 244 } 245 } 246 247 } 248 249 }else{ //失败 250 if(node.isLeaf()){ 251 //不存在删除的对象 252 System.out.println("不存在删除的对象"); 253 return ; 254 } 255 256 //获取子节点 257 BTreeNode childNode = node.childAt(resultSearch.index); 258 259 if(root == node && node.size()==0){ 260 root = childNode; 261 } 262 263 if(childNode.size() >= t){ //如果满足递归条件 264 delete(childNode, key); 265 }else{ 266 //不满足size == t 267 //采取借关键字手段 268 269 BTreeNode subNode = null; 270 int subIndex = 0; 271 //先检测右兄弟节点 272 if(resultSearch.index < node.size()){ 273 if(node.childAt(resultSearch.index+1).size() >=t){ 274 subNode = node.childAt(resultSearch.index+1); 275 subIndex = resultSearch.index + 1; 276 } 277 } 278 //测试左兄弟节点 279 if(subNode == null){ 280 if(resultSearch.index > 0){ 281 if(node.childAt(resultSearch.index-1).size() >= t){ 282 subNode = node.childAt(resultSearch.index-1); 283 subIndex = resultSearch.index - 1; 284 } 285 } 286 } 287 //测试完成后 288 if(subNode != null){ //存在兄弟节点大于等于t情况 289 //判断节点 290 if(subIndex > resultSearch.index){ //右兄弟 291 292 //将右关键字插入自身 293 childNode.insertKey(node.keyAt(subIndex - 1), childNode.size()); 294 node.getKeys().remove(subIndex - 1); 295 node.insertKey(subNode.keyAt(0), subIndex - 1); 296 subNode.getKeys().remove(0); 297 298 //右兄弟非子叶节点,则带有孩子节点 299 if(!subNode.isLeaf()){ 300 childNode.getChildrens().add(subNode.getChildrens().get(0)); 301 subNode.getChildrens().remove(0); 302 } 303 304 }else{ //左兄弟 305 306 //将左关键字插入自身最前位置 307 childNode.insertKey(node.keyAt(subIndex), 0); 308 node.getKeys().remove(subIndex); 309 node.insertKey(subNode.keyAt(subNode.size()-1), subIndex); 310 subNode.getKeys().remove(subNode.size()-1); 311 312 //如果左兄弟非子叶节点 313 if(!subNode.isLeaf()){ 314 childNode.insertChild(subNode.childAt(subNode.size()), 0); 315 subNode.getChildrens().remove(subNode.size()-1); 316 } 317 } 318 delete(childNode, key); 319 320 }else{ 321 322 //该节点的左右兄弟节点关键字都为t-1 323 //选择合并方案 324 if(resultSearch.index < node.size()){ //右兄弟存在 325 326 subNode = node.childAt(resultSearch.index + 1); 327 328 //childNode.getKeys().add(node.keyAt(resultSearch.index + 1)); 329 childNode.getKeys().add(node.keyAt(resultSearch.index)); 330 331 node.getKeys().remove(resultSearch.index.intValue()); 332 node.getChildrens().remove(resultSearch.index.intValue()); 333 334 for(int i=0 ; i<subNode.size() ; i++){ 335 childNode.getKeys().add(subNode.keyAt(i)); 336 } 337 338 if(!subNode.isLeaf()){ 339 for(int k=0 ; k<=subNode.size(); k++){ 340 childNode.getChildrens().add(subNode.childAt(k)); 341 } 342 } 343 344 }else{ //左兄弟存在 345 346 subNode = node.childAt(resultSearch.index - 1); 347 childNode.insertKey(node.keyAt(resultSearch.index-1), 0); 348 node.getKeys().remove(resultSearch.index - 1); 349 node.getChildrens().remove(resultSearch.index-1); 350 351 for(int i=subNode.size()-1 ; i>=0 ; --i){ 352 childNode.insertKey(subNode.keyAt(i), 0); 353 } 354 355 if(!subNode.isLeaf()){ 356 for(int k=subNode.size() ; k>=0 ; --k){ 357 childNode.insertChild(subNode.childAt(k),0); 358 } 359 } 360 361 } 362 if(root == node && node.size() == 0){ 363 root = childNode; 364 } 365 delete(childNode, key); 366 } 367 } 368 369 } 370 } 371 372 } 373 class BTreeNode{ 374 375 /**当前节点keys列表**/ 376 private List<Integer> keys; 377 378 /**当前节点的child列表**/ 379 private List<BTreeNode> childrens; 380 381 /**是否是叶子节点**/ 382 private boolean leaf; 383 384 public BTreeNode(){ 385 keys = new ArrayList<Integer>(); 386 childrens = new ArrayList<BTreeNode>(); 387 leaf = true; 388 } 389 /** 390 * set and get methods 391 * 392 * **/ 393 public List<Integer> getKeys() { 394 return keys; 395 } 396 public void setKeys(List<Integer> keys) { 397 this.keys = keys; 398 } 399 public List<BTreeNode> getChildrens() { 400 return childrens; 401 } 402 public void setChildrens(List<BTreeNode> childrens) { 403 this.childrens = childrens; 404 } 405 public boolean isLeaf() { 406 return leaf; 407 } 408 public void setLeaf(boolean leaf) { 409 this.leaf = leaf; 410 } 411 412 /** 413 * 获取子女节点 414 * @param index 415 * @return 416 */ 417 public BTreeNode childAt(int index){ 418 return childrens.get(index); 419 } 420 /** 421 * 获取关键字 422 * @param index 423 * @return 424 */ 425 public Integer keyAt(int index){ 426 return keys.get(index); 427 } 428 429 /** 430 * 获取节点关键字数量 431 * @return 432 */ 433 public int size(){ 434 return keys.size(); 435 } 436 437 /** 438 * 插入key到指定的index中去 439 * @param key 440 * @param index 441 */ 442 public void insertKey(Integer key, int index){ 443 //插入key到指定的索引位置 444 //保存索引前的所有关键字 445 List<Integer> newlist = new ArrayList<Integer>(); 446 447 for(int i=0; i<index ; i++){ 448 newlist.add(keys.get(i)); 449 } 450 //插入关键字 451 newlist.add(key); 452 //保存索引后多关键字 453 for(int i=index ; i<keys.size() ; i++){ 454 newlist.add(keys.get(i)); 455 } 456 //将新的关键字集合设置为节点关键字集合对象 457 keys = newlist; 458 } 459 460 /** 461 * 插入node子女到指定的index位置 462 * @param node 463 * @param index 464 */ 465 public void insertChild(BTreeNode node, int index){ 466 //插入child 467 List<BTreeNode> newlist = new ArrayList<BTreeNode>(); 468 for(int i=0 ; i< index ; i++){ 469 newlist.add(childrens.get(i)); 470 } 471 newlist.add(node); 472 for(int i=index ; i<childrens.size() ; i++){ 473 newlist.add(childrens.get(i)); 474 } 475 childrens = newlist; 476 } 477 } 478 /** 479 * 查询结果类 480 * result 标识成功的状态 true or false 481 * index 成功后返回元素的索引 482 * 失败后返回元素的插入位置 483 * @author lingfeng 484 */ 485 class ResultSearch{ 486 487 public Integer index; 488 public boolean result; 489 490 public ResultSearch(boolean rs, Integer i){ 491 index = i; 492 result = rs; 493 } 494 }