自己写的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 }
时间: 2024-10-10 02:22:20

自己写的java实现的多路搜索树 B-Tree的相关文章

Java实现二叉搜索树节点的删除

前言: 之前写过一篇关于二叉搜索树的博客:Java对二叉搜索树进行插入.查找.遍历.最大值和最小值的操作  二叉查找树重要性质: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值: (3)左.右子树也分别为二叉排序树: 如图: 这次我想分享的是二叉搜索树中节点是如何删除的,删除节点是二叉搜索树常用的一般操作中最复杂的,删除节点要从查找要删除的节点开始入手 ,找到节点后,这个要删除的节点可能会有三种情况需要考虑: 1

Java实现二叉搜索树

原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11406176.html 尝试一下用Java实现二叉搜索树/二叉查找树,记录自己的学习历程. 1. 首先先来设计实现一下节点Node. ??一个二叉树的节点需要以下几个元素: key 关键字 value 节点的值(key也可以代替value) parent 父节点 leftChildren 左儿子节点 rightChildren 右儿子节点 那就开始吧! /** * 节点 */ class N

[数据结构]二叉搜索树(BST) VS 平衡二叉排序树(AVL) VS B树(平衡多路搜索树) VS B+树 VS 红黑树(平衡二叉B树)

1 二叉排序树/二叉查找树/Binary Sort Tree 1种对排序和查找都很有用的特殊二叉树 叉排序树的弊端的解决方案:平衡二叉树 二叉排序树必须满足的3条性质(或是具有如下特征的二叉树) 若它的左子树不为空,则:左子树上所有结点的值< 它根结点的值 若它的右子树不为空,则:右子树上所有结点的值 > 它根结点的值 它的左子树.右子树也分别为二叉排序树(递归性) (按照如上定义,即: 1 无键值相等的结点 2 中序遍历一颗二叉树时,可得一个结点值递增的有序序列) 2 平衡二叉排序树/Bal

自己写一个java.lang.reflect.Proxy代理的实现

前言 Java设计模式9:代理模式一文中,讲到了动态代理,动态代理里面用到了一个类就是java.lang.reflect.Proxy,这个类是根据代理内容为传入的接口生成代理用的.本文就自己写一个Proxy类出来,功能和java.lang.reflect.Proxy一样,传入接口.代理内容,生成代理. 抛砖引玉吧,个人觉得自己写一些JDK里面的那些类挺好的,写一遍和看一遍真的是两个不同的概念,写一遍既加深了对于这些类的理解.提升了自己的写代码水平,也可以在写完之后对比一下自己的实现有哪些写得不好

写给java初学者

写给java初学自学者(一) 开篇直奔主题,java学习个人感觉分为两种途径,第一种是在学校,在培训机构等地方学习,有人指导;第二种是自学,通过视频,书籍,朋友等完成学习. 本文适合 自学,且基础薄弱或者无基础的人. 下面先就几个常见问题做个问答式讲解: 1. 学java需要基础吗?学java需要英语什么水平? 当你问这些问题前,首先要给自己一个定位.你学java是用来干什么的? 如果你只是想找个这方面的工作,进入这个个行业,那么没有基础,英语水平一般(例如public static void

代写程序|java二叉树字典查询(qq 928900200)

This assignment will help you practice and understand better the Binary Tree and Binary Search Tree data structures, their operations and implementations. You are to design a small dictionary using the Binary Search Tree data structure. Each entry ma

(今天是第一天开始写博客)写一下java集合类使用中容易出现的错误,慢慢积累

java中经常使用到的集合类有:Set,Map,List 关于Set,我犯了好几次的错误: ①Set1=Set2,实际上是让Set1也指向Set2了,如果此时Set2改变,那么Set1也会改变.算是一种地址赋值吧~ 如果想要让Set1中的元素与Set2中的元素相同,应该使用Set1.addAll(Set2). 其实,还有字符串也是一样的. if(str=="#"){...} 这样的写法,是不对的,条件不会成立的,因为"#"的地址与str的地址不一样. 正确:if(s

软件测试第二次作业 - 写一个Java程序,用于分析一个字符串中各个单词出现的频率,并将单词和它出现的频率输出显示。

题目一: 1. 写一个Java程序,用于分析一个字符串中各个单词出现的频率,并将单词和它出现的频率输出显示.(单词之间用空格隔开,如“Hello World My First Unit Test”): 2. 编写单元测试进行测试: 3. 用ElcEmma查看代码覆盖率,要求覆盖率达到100%. Demo类: 1 import java.util.HashMap; 2 import java.util.Iterator; 3 import java.util.Map; 4 import java.

手写快速排序(java实现)

手写快速排序(java实现) 时间复杂度: O(nlogn) 快速排序原理: 定义一个基准元素base(我这里定义的是最左面的元素定位基准元素) 定义两个变量i和j j先从右向左遍历,找到第一个比base小的数就停止 i再从左向右便利找到第一个比base大的数停止 交换i和j指向的元素 直到i和j指向同一个元素,将这个元素与基准元素交换 递归求解即可 图解:(排序过程) 注意: 上图为第一次快速排序的过程,递归过程和上图一致(没有给出) 每一次的排序结果: 现在在火车上没有给出具体每次排序结果,