B树的java实现

B树是为磁盘或其他直接存取辅助存储设置而设计的一种平衡查找树。其能够有效降低磁盘I/O操作次数。许多数据库系统使用B树或B树的变形来储存信息。清明节这几天闲来无事,参考《算法导论》第二版第十八章的思想使用java语言实现了一颗简单的B树,在此跟大家分享下,就当是抛砖引玉,欢迎大家跟我讨论。

[java] 
view plain
copy

  1. package com.discover;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Comparator;  
  5. import java.util.LinkedList;  
  6. import java.util.List;  
  7. import java.util.Queue;  
  8. import java.util.Random;  
  9.   
  10. import org.apache.commons.logging.Log;  
  11. import org.apache.commons.logging.LogFactory;  
  12.   
  13. /** 
  14.  * 一颗B树的简单实现。 
  15.  * <p/> 
  16.  * 其实现原理参考《算法导论》第二版第十八章。 
  17.  * <p/> 
  18.  * 如果大家想读懂这些源代码,不妨先看看上述章节。 
  19.  * <p/> 
  20.  * TODO B树如何存储在文件系统中,大家不妨想想 
  21.  *  
  22.  * @author WangPing 欢迎转载,转载请标明原文地址 
  23.  * 
  24.  * @param <K> – 键类型 
  25.  * @param <V> – 值类型 
  26.  */  
  27. public class BTree<K, V>  
  28. {  
  29.     private static Log logger = LogFactory.getLog(BTree.class);  
  30.       
  31.     /** 
  32.      * B树节点中的键值对。 
  33.      * <p/>  
  34.      * B树的节点中存储的是键值对。 
  35.      * 通过键访问值。 
  36.      * 
  37.      * @param <K> – 键类型 
  38.      * @param <V> – 值类型 
  39.      */  
  40.     private static class Entry<K, V>  
  41.     {  
  42.         private K key;  
  43.         private V value;  
  44.           
  45.         public Entry(K k, V v)  
  46.         {  
  47.             this.key = k;  
  48.             this.value = v;  
  49.         }  
  50.           
  51.         public K getKey()  
  52.         {  
  53.             return key;  
  54.         }  
  55.           
  56.         public V getValue()  
  57.         {  
  58.             return value;  
  59.         }  
  60.           
  61.         public void setValue(V value)  
  62.         {  
  63.             this.value = value;  
  64.         }  
  65.           
  66.         @Override  
  67.         public String toString()  
  68.         {  
  69.             return key + “:” + value;  
  70.         }  
  71.     }  
  72.       
  73.     /** 
  74.      * 在B树节点中搜索给定键值的返回结果。 
  75.      * <p/>  
  76.      * 该结果有两部分组成。第一部分表示此次查找是否成功, 
  77.      * 如果查找成功,第二部分表示给定键值在B树节点中的位置, 
  78.      * 如果查找失败,第二部分表示给定键值应该插入的位置。 
  79.      */  
  80.     private static class SearchResult<V>  
  81.     {  
  82.         private boolean exist;  
  83.         private int index;  
  84.         private V value;  
  85.           
  86.         public SearchResult(boolean exist, int index)  
  87.         {  
  88.             this.exist = exist;  
  89.             this.index = index;  
  90.         }  
  91.           
  92.         public SearchResult(boolean exist, int index, V value)  
  93.         {  
  94.             this(exist, index);  
  95.             this.value = value;  
  96.         }  
  97.           
  98.         public boolean isExist()  
  99.         {  
  100.             return exist;  
  101.         }  
  102.           
  103.         public int getIndex()  
  104.         {  
  105.             return index;  
  106.         }  
  107.           
  108.         public V getValue()   
  109.         {  
  110.             return value;  
  111.         }  
  112.     }  
  113.       
  114.     /** 
  115.      * B树中的节点。 
  116.      *  
  117.      * TODO 需要考虑并发情况下的存取。 
  118.      */  
  119.     private static class BTreeNode<K, V>  
  120.     {  
  121.         /** 节点的项,按键非降序存放 */  
  122.         private List<Entry<K,V>> entrys;  
  123.         /** 内节点的子节点 */  
  124.         private List<BTreeNode<K, V>> children;  
  125.         /** 是否为叶子节点 */  
  126.         private boolean leaf;  
  127.         /** 键的比较函数对象 */  
  128.         private Comparator<K> kComparator;  
  129.           
  130.         private BTreeNode()  
  131.         {  
  132.             entrys = new ArrayList<Entry<K, V>>();  
  133.             children = new ArrayList<BTreeNode<K, V>>();  
  134.             leaf = false;  
  135.         }  
  136.           
  137.         public BTreeNode(Comparator<K> kComparator)  
  138.         {  
  139.             this();  
  140.             this.kComparator = kComparator;  
  141.         }  
  142.           
  143.         public boolean isLeaf()  
  144.         {  
  145.             return leaf;  
  146.         }  
  147.           
  148.         public void setLeaf(boolean leaf)  
  149.         {  
  150.             this.leaf = leaf;  
  151.         }  
  152.           
  153.         /** 
  154.          * 返回项的个数。如果是非叶子节点,根据B树的定义, 
  155.          * 该节点的子节点个数为({@link #size()} + 1)。 
  156.          *  
  157.          * @return 关键字的个数 
  158.          */  
  159.         public int size()  
  160.         {  
  161.             return entrys.size();  
  162.         }  
  163.           
  164.         @SuppressWarnings(“unchecked”)  
  165.         int compare(K key1, K key2)  
  166.         {  
  167.             return kComparator == null ? ((Comparable<K>)key1).compareTo(key2) : kComparator.compare(key1, key2);  
  168.         }  
  169.           
  170.         /** 
  171.          * 在节点中查找给定的键。 
  172.          * 如果节点中存在给定的键,则返回一个<code>SearchResult</code>, 
  173.          * 标识此次查找成功,给定的键在节点中的索引和给定的键关联的值; 
  174.          * 如果不存在,则返回<code>SearchResult</code>, 
  175.          * 标识此次查找失败,给定的键应该插入的位置,该键的关联值为null。 
  176.          * <p/> 
  177.          * 如果查找失败,返回结果中的索引域为[0, {@link #size()}]; 
  178.          * 如果查找成功,返回结果中的索引域为[0, {@link #size()} – 1] 
  179.          * <p/> 
  180.          * 这是一个二分查找算法,可以保证时间复杂度为O(log(t))。 
  181.          *  
  182.          * @param key – 给定的键值 
  183.          * @return – 查找结果 
  184.          */  
  185.         public SearchResult<V> searchKey(K key)  
  186.         {  
  187.             int low = 0;  
  188.             int high = entrys.size() – 1;  
  189.             int mid = 0;  
  190.             while(low <= high)  
  191.             {  
  192.                 mid = (low + high) / 2// 先这么写吧,BTree实现中,l+h不可能溢出  
  193.                 Entry<K, V> entry = entrys.get(mid);  
  194.                 if(compare(entry.getKey(), key) == 0// entrys.get(mid).getKey() == key  
  195.                     break;  
  196.                 else if(compare(entry.getKey(), key) > 0// entrys.get(mid).getKey() > key  
  197.                     high = mid – 1;  
  198.                 else // entry.get(mid).getKey() < key  
  199.                     low = mid + 1;  
  200.             }  
  201.             boolean result = false;  
  202.             int index = 0;  
  203.             V value = null;  
  204.             if(low <= high) // 说明查找成功  
  205.             {  
  206.                 result = true;  
  207.                 index = mid; // index表示元素所在的位置  
  208.                 value = entrys.get(index).getValue();  
  209.             }  
  210.             else  
  211.             {  
  212.                 result = false;  
  213.                 index = low; // index表示元素应该插入的位置  
  214.             }  
  215.             return new SearchResult<V>(result, index, value);  
  216.         }  
  217.           
  218.         /** 
  219.          * 将给定的项追加到节点的末尾, 
  220.          * 你需要自己确保调用该方法之后,节点中的项还是 
  221.          * 按照关键字以非降序存放。 
  222.          *  
  223.          * @param entry – 给定的项 
  224.          */  
  225.         public void addEntry(Entry<K, V> entry)  
  226.         {  
  227.             entrys.add(entry);  
  228.         }  
  229.           
  230.         /** 
  231.          * 删除给定索引的<code>entry</code>。 
  232.          * <p/> 
  233.          * 你需要自己保证给定的索引是合法的。 
  234.          *  
  235.          * @param index – 给定的索引 
  236.          * @param 给定索引处的项 
  237.          */  
  238.         public Entry<K, V> removeEntry(int index)  
  239.         {  
  240.             return entrys.remove(index);  
  241.         }  
  242.           
  243.         /** 
  244.          * 得到节点中给定索引的项。 
  245.          * <p/> 
  246.          * 你需要自己保证给定的索引是合法的。 
  247.          *  
  248.          * @param index – 给定的索引 
  249.          * @return 节点中给定索引的项 
  250.          */  
  251.         public Entry<K, V> entryAt(int index)  
  252.         {  
  253.             return entrys.get(index);  
  254.         }  
  255.           
  256.         /** 
  257.          * 如果节点中存在给定的键,则更新其关联的值。 
  258.          * 否则插入。 
  259.          *  
  260.          * @param entry – 给定的项 
  261.          * @return null,如果节点之前不存在给定的键,否则返回给定键之前关联的值 
  262.          */  
  263.         public V putEntry(Entry<K, V> entry)  
  264.         {  
  265.             SearchResult<V> result = searchKey(entry.getKey());  
  266.             if(result.isExist())  
  267.             {  
  268.                 V oldValue = entrys.get(result.getIndex()).getValue();  
  269.                 entrys.get(result.getIndex()).setValue(entry.getValue());  
  270.                 return oldValue;  
  271.             }  
  272.             else  
  273.             {  
  274.                 insertEntry(entry, result.getIndex());  
  275.                 return null;  
  276.             }  
  277.         }  
  278.           
  279.         /** 
  280.          * 在该节点中插入给定的项, 
  281.          * 该方法保证插入之后,其键值还是以非降序存放。 
  282.          * <p/> 
  283.          * 不过该方法的时间复杂度为O(t)。 
  284.          * <p/> 
  285.          * <b>注意:</b>B树中不允许键值重复。 
  286.          *  
  287.          * @param entry – 给定的键值 
  288.          * @return true,如果插入成功,false,如果插入失败 
  289.          */  
  290.         public boolean insertEntry(Entry<K, V> entry)  
  291.         {  
  292.             SearchResult<V> result = searchKey(entry.getKey());  
  293.             if(result.isExist())  
  294.                 return false;  
  295.             else  
  296.             {  
  297.                 insertEntry(entry, result.getIndex());  
  298.                 return true;  
  299.             }  
  300.         }  
  301.           
  302.         /** 
  303.          * 在该节点中给定索引的位置插入给定的项, 
  304.          * 你需要自己保证项插入了正确的位置。 
  305.          *  
  306.          * @param key – 给定的键值 
  307.          * @param index – 给定的索引 
  308.          */  
  309.         public void insertEntry(Entry<K, V> entry, int index)  
  310.         {  
  311.             /* 
  312.              * 通过新建一个ArrayList来实现插入真的很恶心,先这样吧 
  313.              * 要是有类似C中的reallocate就好了。 
  314.              */  
  315.             List<Entry<K, V>> newEntrys = new ArrayList<Entry<K, V>>();  
  316.             int i = 0;  
  317.             // index = 0或者index = keys.size()都没有问题  
  318.             for(; i < index; ++ i)  
  319.                 newEntrys.add(entrys.get(i));  
  320.             newEntrys.add(entry);  
  321.             for(; i < entrys.size(); ++ i)  
  322.                 newEntrys.add(entrys.get(i));  
  323.             entrys.clear();  
  324.             entrys = newEntrys;  
  325.         }  
  326.           
  327.         /** 
  328.          * 返回节点中给定索引的子节点。 
  329.          * <p/> 
  330.          * 你需要自己保证给定的索引是合法的。 
  331.          *  
  332.          * @param index – 给定的索引 
  333.          * @return 给定索引对应的子节点 
  334.          */  
  335.         public BTreeNode<K, V> childAt(int index)  
  336.         {  
  337.             if(isLeaf())  
  338.                 throw new UnsupportedOperationException(“Leaf node doesn’t have children.”);  
  339.             return children.get(index);  
  340.         }  
  341.           
  342.         /** 
  343.          * 将给定的子节点追加到该节点的末尾。 
  344.          *  
  345.          * @param child – 给定的子节点 
  346.          */  
  347.         public void addChild(BTreeNode<K, V> child)  
  348.         {  
  349.             children.add(child);  
  350.         }  
  351.           
  352.         /** 
  353.          * 删除该节点中给定索引位置的子节点。 
  354.          * </p> 
  355.          * 你需要自己保证给定的索引是合法的。 
  356.          *  
  357.          * @param index – 给定的索引 
  358.          */  
  359.         public void removeChild(int index)  
  360.         {  
  361.             children.remove(index);  
  362.         }  
  363.           
  364.         /** 
  365.          * 将给定的子节点插入到该节点中给定索引 
  366.          * 的位置。 
  367.          *  
  368.          * @param child – 给定的子节点 
  369.          * @param index – 子节点带插入的位置 
  370.          */  
  371.         public void insertChild(BTreeNode<K, V> child, int index)  
  372.         {  
  373.             List<BTreeNode<K, V>> newChildren = new ArrayList<BTreeNode<K, V>>();  
  374.             int i = 0;  
  375.             for(; i < index; ++ i)  
  376.                 newChildren.add(children.get(i));  
  377.             newChildren.add(child);  
  378.             for(; i < children.size(); ++ i)  
  379.                 newChildren.add(children.get(i));  
  380.             children = newChildren;  
  381.         }  
  382.     }  
  383.       
  384.     private static final int DEFAULT_T = 2;  
  385.       
  386.     /** B树的根节点 */  
  387.     private BTreeNode<K, V> root;  
  388.     /** 根据B树的定义,B树的每个非根节点的关键字数n满足(t – 1) <= n <= (2t – 1) */  
  389.     private int t = DEFAULT_T;  
  390.     /** 非根节点中最小的键值数 */  
  391.     private int minKeySize = t – 1;  
  392.     /** 非根节点中最大的键值数 */  
  393.     private int maxKeySize = 2*t – 1;  
  394.     /** 键的比较函数对象 */  
  395.     private Comparator<K> kComparator;  
  396.       
  397.     /** 
  398.      * 构造一颗B树,键值采用采用自然排序方式 
  399.      */  
  400.     public BTree()  
  401.     {  
  402.         root = new BTreeNode<K, V>();  
  403.         root.setLeaf(true);  
  404.     }  
  405.       
  406.     public BTree(int t)  
  407.     {  
  408.         this();  
  409.         this.t = t;  
  410.         minKeySize = t – 1;  
  411.         maxKeySize = 2*t – 1;  
  412.     }  
  413.       
  414.     /** 
  415.      * 以给定的键值比较函数对象构造一颗B树。 
  416.      *  
  417.      * @param kComparator – 键值的比较函数对象 
  418.      */  
  419.     public BTree(Comparator<K> kComparator)  
  420.     {  
  421.         root = new BTreeNode<K, V>(kComparator);  
  422.         root.setLeaf(true);  
  423.         this.kComparator = kComparator;  
  424.     }  
  425.       
  426.     public BTree(Comparator<K> kComparator, int t)  
  427.     {  
  428.         this(kComparator);  
  429.         this.t = t;  
  430.         minKeySize = t – 1;  
  431.         maxKeySize = 2*t – 1;  
  432.     }  
  433.       
  434.     @SuppressWarnings(“unchecked”)  
  435.     int compare(K key1, K key2)  
  436.     {  
  437.         return kComparator == null ? ((Comparable<K>)key1).compareTo(key2) : kComparator.compare(key1, key2);  
  438.     }  
  439.       
  440.     /** 
  441.      * 搜索给定的键。 
  442.      *  
  443.      * @param key – 给定的键值 
  444.      * @return 键关联的值,如果存在,否则null 
  445.      */  
  446.     public V search(K key)  
  447.     {  
  448.         return search(root, key);  
  449.     }  
  450.       
  451.     /** 
  452.      * 在以给定节点为根的子树中,递归搜索 
  453.      * 给定的<code>key</code> 
  454.      *  
  455.      * @param node – 子树的根节点 
  456.      * @param key – 给定的键值 
  457.      * @return 键关联的值,如果存在,否则null 
  458.      */  
  459.     private V search(BTreeNode<K, V> node, K key)  
  460.     {  
  461.         SearchResult<V> result = node.searchKey(key);  
  462.         if(result.isExist())  
  463.             return result.getValue();  
  464.         else  
  465.         {  
  466.             if(node.isLeaf())  
  467.                 return null;  
  468.             else   
  469.                 search(node.childAt(result.getIndex()), key);  
  470.                   
  471.         }  
  472.         return null;  
  473.     }  
  474.       
  475.     /** 
  476.      * 分裂一个满子节点<code>childNode</code>。 
  477.      * <p/> 
  478.      * 你需要自己保证给定的子节点是满节点。 
  479.      *  
  480.      * @param parentNode – 父节点 
  481.      * @param childNode – 满子节点 
  482.      * @param index – 满子节点在父节点中的索引 
  483.      */  
  484.     private void splitNode(BTreeNode<K, V> parentNode, BTreeNode<K, V> childNode, int index)  
  485.     {  
  486.         assert childNode.size() == maxKeySize;  
  487.           
  488.         BTreeNode<K, V> siblingNode = new BTreeNode<K, V>(kComparator);  
  489.         siblingNode.setLeaf(childNode.isLeaf());  
  490.         // 将满子节点中索引为[t, 2t – 2]的(t – 1)个项插入新的节点中  
  491.         for(int i = 0; i < minKeySize; ++ i)  
  492.             siblingNode.addEntry(childNode.entryAt(t + i));  
  493.         // 提取满子节点中的中间项,其索引为(t – 1)  
  494.         Entry<K, V> entry = childNode.entryAt(t – 1);  
  495.         // 删除满子节点中索引为[t – 1, 2t – 2]的t个项  
  496.         for(int i = maxKeySize – 1; i >= t – 1; — i)  
  497.             childNode.removeEntry(i);  
  498.         if(!childNode.isLeaf()) // 如果满子节点不是叶节点,则还需要处理其子节点  
  499.         {  
  500.             // 将满子节点中索引为[t, 2t – 1]的t个子节点插入新的节点中  
  501.             for(int i = 0; i < minKeySize + 1; ++ i)  
  502.                 siblingNode.addChild(childNode.childAt(t + i));  
  503.             // 删除满子节点中索引为[t, 2t – 1]的t个子节点  
  504.             for(int i = maxKeySize; i >= t; — i)  
  505.                 childNode.removeChild(i);  
  506.         }  
  507.         // 将entry插入父节点  
  508.         parentNode.insertEntry(entry, index);  
  509.         // 将新节点插入父节点  
  510.         parentNode.insertChild(siblingNode, index + 1);  
  511.     }  
  512.       
  513.     /** 
  514.      * 在一个非满节点中插入给定的项。 
  515.      *  
  516.      * @param node – 非满节点 
  517.      * @param entry – 给定的项 
  518.      * @return true,如果B树中不存在给定的项,否则false 
  519.      */  
  520.     private boolean insertNotFull(BTreeNode<K, V> node, Entry<K, V> entry)  
  521.     {  
  522.         assert node.size() < maxKeySize;  
  523.           
  524.         if(node.isLeaf()) // 如果是叶子节点,直接插入  
  525.             return node.insertEntry(entry);  
  526.         else  
  527.         {  
  528.             /* 找到entry在给定节点应该插入的位置,那么entry应该插入 
  529.              * 该位置对应的子树中 
  530.              */  
  531.             SearchResult<V> result = node.searchKey(entry.getKey());  
  532.             // 如果存在,则直接返回失败  
  533.             if(result.isExist())  
  534.                 return false;  
  535.             BTreeNode<K, V> childNode = node.childAt(result.getIndex());  
  536.             if(childNode.size() == 2*t – 1// 如果子节点是满节点  
  537.             {  
  538.                 // 则先分裂  
  539.                 splitNode(node, childNode, result.getIndex());  
  540.                 /* 如果给定entry的键大于分裂之后新生成项的键,则需要插入该新项的右边, 
  541.                  * 否则左边。 
  542.                  */  
  543.                 if(compare(entry.getKey(), node.entryAt(result.getIndex()).getKey()) > 0)  
  544.                     childNode = node.childAt(result.getIndex() + 1);  
  545.             }  
  546.             return insertNotFull(childNode, entry);  
  547.         }  
  548.     }  
  549.       
  550.     /** 
  551.      * 在B树中插入给定的键值对。 
  552.      *  
  553.      * @param key – 键 
  554.      * @param value – 值 
  555.      * @param true,如果B树中不存在给定的项,否则false 
  556.      */  
  557.     public boolean insert(K key, V value)  
  558.     {  
  559.         if(root.size() == maxKeySize) // 如果根节点满了,则B树长高  
  560.         {  
  561.             BTreeNode<K, V> newRoot = new BTreeNode<K, V>(kComparator);  
  562.             newRoot.setLeaf(false);  
  563.             newRoot.addChild(root);  
  564.             splitNode(newRoot, root, 0);  
  565.             root = newRoot;  
  566.         }  
  567.         return insertNotFull(root, new Entry<K, V>(key, value));  
  568.     }  
  569.       
  570.     /** 
  571.      * 如果存在给定的键,则更新键关联的值, 
  572.      * 否则插入给定的项。 
  573.      *  
  574.      * @param node – 非满节点 
  575.      * @param entry – 给定的项 
  576.      * @return true,如果B树中不存在给定的项,否则false 
  577.      */  
  578.     private V putNotFull(BTreeNode<K, V> node, Entry<K, V> entry)  
  579.     {  
  580.         assert node.size() < maxKeySize;  
  581.           
  582.         if(node.isLeaf()) // 如果是叶子节点,直接插入  
  583.             return node.putEntry(entry);  
  584.         else  
  585.         {  
  586.             /* 找到entry在给定节点应该插入的位置,那么entry应该插入 
  587.              * 该位置对应的子树中 
  588.              */  
  589.             SearchResult<V> result = node.searchKey(entry.getKey());  
  590.             // 如果存在,则更新  
  591.             if(result.isExist())  
  592.                 return node.putEntry(entry);  
  593.             BTreeNode<K, V> childNode = node.childAt(result.getIndex());  
  594.             if(childNode.size() == 2*t – 1// 如果子节点是满节点  
  595.             {  
  596.                 // 则先分裂  
  597.                 splitNode(node, childNode, result.getIndex());  
  598.                 /* 如果给定entry的键大于分裂之后新生成项的键,则需要插入该新项的右边, 
  599.                  * 否则左边。 
  600.                  */  
  601.                 if(compare(entry.getKey(), node.entryAt(result.getIndex()).getKey()) > 0)  
  602.                     childNode = node.childAt(result.getIndex() + 1);  
  603.             }  
  604.             return putNotFull(childNode, entry);  
  605.         }  
  606.     }  
  607.       
  608.     /** 
  609.      * 如果B树中存在给定的键,则更新值。 
  610.      * 否则插入。 
  611.      *  
  612.      * @param key – 键 
  613.      * @param value – 值 
  614.      * @return 如果B树中存在给定的键,则返回之前的值,否则null 
  615.      */  
  616.     public V put(K key, V value)  
  617.     {  
  618.         if(root.size() == maxKeySize) // 如果根节点满了,则B树长高  
  619.         {  
  620.             BTreeNode<K, V> newRoot = new BTreeNode<K, V>(kComparator);  
  621.             newRoot.setLeaf(false);  
  622.             newRoot.addChild(root);  
  623.             splitNode(newRoot, root, 0);  
  624.             root = newRoot;  
  625.         }  
  626.         return putNotFull(root, new Entry<K, V>(key, value));  
  627.     }  
  628.       
  629.     /** 
  630.      * 从B树中删除一个与给定键关联的项。 
  631.      *  
  632.      * @param key – 给定的键 
  633.      * @return 如果B树中存在给定键关联的项,则返回删除的项,否则null 
  634.      */  
  635.     public Entry<K, V> delete(K key)  
  636.     {  
  637.         return delete(root, key);  
  638.     }  
  639.       
  640.     /** 
  641.      * 从以给定<code>node</code>为根的子树中删除与给定键关联的项。 
  642.      * <p/> 
  643.      * 删除的实现思想请参考《算法导论》第二版的第18章。 
  644.      *  
  645.      * @param node – 给定的节点 
  646.      * @param key – 给定的键 
  647.      * @return 如果B树中存在给定键关联的项,则返回删除的项,否则null 
  648.      */  
  649.     private Entry<K, V> delete(BTreeNode<K, V> node, K key)  
  650.     {  
  651.         // 该过程需要保证,对非根节点执行删除操作时,其关键字个数至少为t。  
  652.         assert node.size() >= t || node == root;  
  653.           
  654.         SearchResult<V> result = node.searchKey(key);  
  655.         /* 
  656.          * 因为这是查找成功的情况,0 <= result.getIndex() <= (node.size() – 1), 
  657.          * 因此(result.getIndex() + 1)不会溢出。 
  658.          */  
  659.         if(result.isExist())  
  660.         {  
  661.             // 1.如果关键字在节点node中,并且是叶节点,则直接删除。  
  662.             if(node.isLeaf())  
  663.                 return node.removeEntry(result.getIndex());  
  664.             else  
  665.             {  
  666.                 // 2.a 如果节点node中前于key的子节点包含至少t个项  
  667.                 BTreeNode<K, V> leftChildNode = node.childAt(result.getIndex());  
  668.                 if(leftChildNode.size() >= t)  
  669.                 {  
  670.                     // 使用leftChildNode中的最后一个项代替node中需要删除的项  
  671.                     node.removeEntry(result.getIndex());  
  672.                     node.insertEntry(leftChildNode.entryAt(leftChildNode.size() – 1), result.getIndex());  
  673.                     // 递归删除左子节点中的最后一个项  
  674.                     return delete(leftChildNode, leftChildNode.entryAt(leftChildNode.size() – 1).getKey());  
  675.                 }  
  676.                 else  
  677.                 {  
  678.                     // 2.b 如果节点node中后于key的子节点包含至少t个关键字  
  679.                     BTreeNode<K, V> rightChildNode = node.childAt(result.getIndex() + 1);  
  680.                     if(rightChildNode.size() >= t)  
  681.                     {  
  682.                         // 使用rightChildNode中的第一个项代替node中需要删除的项  
  683.                         node.removeEntry(result.getIndex());  
  684.                         node.insertEntry(rightChildNode.entryAt(0), result.getIndex());  
  685.                         // 递归删除右子节点中的第一个项  
  686.                         return delete(rightChildNode, rightChildNode.entryAt(0).getKey());  
  687.                     }  
  688.                     else // 2.c 前于key和后于key的子节点都只包含t-1个项  
  689.                     {  
  690.                         Entry<K, V> deletedEntry = node.removeEntry(result.getIndex());  
  691.                         node.removeChild(result.getIndex() + 1);  
  692.                         // 将node中与key关联的项和rightChildNode中的项合并进leftChildNode  
  693.                         leftChildNode.addEntry(deletedEntry);  
  694.                         for(int i = 0; i < rightChildNode.size(); ++ i)  
  695.                             leftChildNode.addEntry(rightChildNode.entryAt(i));  
  696.                         // 将rightChildNode中的子节点合并进leftChildNode,如果有的话  
  697.                         if(!rightChildNode.isLeaf())  
  698.                         {  
  699.                             for(int i = 0; i <= rightChildNode.size(); ++ i)  
  700.                                 leftChildNode.addChild(rightChildNode.childAt(i));  
  701.                         }  
  702.                         return delete(leftChildNode, key);  
  703.                     }  
  704.                 }  
  705.             }  
  706.         }  
  707.         else  
  708.         {  
  709.             /* 
  710.              * 因为这是查找失败的情况,0 <= result.getIndex() <= node.size(), 
  711.              * 因此(result.getIndex() + 1)会溢出。 
  712.              */  
  713.             if(node.isLeaf()) // 如果关键字不在节点node中,并且是叶节点,则什么都不做,因为该关键字不在该B树中  
  714.             {  
  715.                 logger.info(“The key: “ + key + ” isn’t in this BTree.”);  
  716.                 return null;  
  717.             }  
  718.             BTreeNode<K, V> childNode = node.childAt(result.getIndex());  
  719.             if(childNode.size() >= t) // // 如果子节点有不少于t个项,则递归删除  
  720.                 return delete(childNode, key);  
  721.             else // 3  
  722.             {  
  723.                 // 先查找右边的兄弟节点  
  724.                 BTreeNode<K, V> siblingNode = null;  
  725.                 int siblingIndex = –1;  
  726.                 if(result.getIndex() < node.size()) // 存在右兄弟节点  
  727.                 {  
  728.                     if(node.childAt(result.getIndex() + 1).size() >= t)  
  729.                     {  
  730.                         siblingNode = node.childAt(result.getIndex() + 1);  
  731.                         siblingIndex = result.getIndex() + 1;  
  732.                     }  
  733.                 }  
  734.                 // 如果右边的兄弟节点不符合条件,则试试左边的兄弟节点  
  735.                 if(siblingNode == null)  
  736.                 {  
  737.                     if(result.getIndex() > 0// 存在左兄弟节点  
  738.                     {  
  739.                         if(node.childAt(result.getIndex() – 1).size() >= t)  
  740.                         {  
  741.                             siblingNode = node.childAt(result.getIndex() – 1);  
  742.                             siblingIndex = result.getIndex() – 1;  
  743.                         }  
  744.                     }  
  745.                 }  
  746.                 // 3.a 有一个相邻兄弟节点至少包含t个项  
  747.                 if(siblingNode != null)  
  748.                 {  
  749.                     if(siblingIndex < result.getIndex()) // 左兄弟节点满足条件  
  750.                     {  
  751.                         childNode.insertEntry(node.entryAt(siblingIndex), 0);  
  752.                         node.removeEntry(siblingIndex);  
  753.                         node.insertEntry(siblingNode.entryAt(siblingNode.size() – 1), siblingIndex);  
  754.                         siblingNode.removeEntry(siblingNode.size() – 1);  
  755.                         // 将左兄弟节点的最后一个孩子移到childNode  
  756.                         if(!siblingNode.isLeaf())  
  757.                         {  
  758.                             childNode.insertChild(siblingNode.childAt(siblingNode.size()), 0);  
  759.                             siblingNode.removeChild(siblingNode.size());  
  760.                         }  
  761.                     }  
  762.                     else // 右兄弟节点满足条件  
  763.                     {  
  764.                         childNode.insertEntry(node.entryAt(result.getIndex()), childNode.size() – 1);  
  765.                         node.removeEntry(result.getIndex());  
  766.                         node.insertEntry(siblingNode.entryAt(0), result.getIndex());  
  767.                         siblingNode.removeEntry(0);  
  768.                         // 将右兄弟节点的第一个孩子移到childNode  
  769.                         // childNode.insertChild(siblingNode.childAt(0), childNode.size() + 1);  
  770.                         if(!siblingNode.isLeaf())  
  771.                         {  
  772.                             childNode.addChild(siblingNode.childAt(0));  
  773.                             siblingNode.removeChild(0);  
  774.                         }  
  775.                     }  
  776.                     return delete(childNode, key);  
  777.                 }  
  778.                 else // 3.b 如果其相邻左右节点都包含t-1个项  
  779.                 {  
  780.                     if(result.getIndex() < node.size()) // 存在右兄弟,直接在后面追加  
  781.                     {  
  782.                         BTreeNode<K, V> rightSiblingNode = node.childAt(result.getIndex() + 1);  
  783.                         childNode.addEntry(node.entryAt(result.getIndex()));  
  784.                         node.removeEntry(result.getIndex());  
  785.                         node.removeChild(result.getIndex() + 1);  
  786.                         for(int i = 0; i < rightSiblingNode.size(); ++ i)  
  787.                             childNode.addEntry(rightSiblingNode.entryAt(i));  
  788.                         if(!rightSiblingNode.isLeaf())  
  789.                         {  
  790.                             for(int i = 0; i <= rightSiblingNode.size(); ++ i)  
  791.                                 childNode.addChild(rightSiblingNode.childAt(i));  
  792.                         }  
  793.                     }  
  794.                     else // 存在左节点,在前面插入  
  795.                     {  
  796.                         BTreeNode<K, V> leftSiblingNode = node.childAt(result.getIndex() – 1);  
  797.                         childNode.insertEntry(node.entryAt(result.getIndex() – 1), 0);  
  798.                         node.removeEntry(result.getIndex() – 1);  
  799.                         node.removeChild(result.getIndex() – 1);  
  800.                         for(int i = leftSiblingNode.size() – 1; i >= 0; — i)  
  801.                             childNode.insertEntry(leftSiblingNode.entryAt(i), 0);  
  802.                         if(!leftSiblingNode.isLeaf())  
  803.                         {  
  804.                             for(int i = leftSiblingNode.size(); i >= 0; — i)  
  805.                                 childNode.insertChild(leftSiblingNode.childAt(i), 0);  
  806.                         }  
  807.                     }  
  808.                     // 如果node是root并且node不包含任何项了  
  809.                     if(node == root && node.size() == 0)  
  810.                         root = childNode;  
  811.                     return delete(childNode, key);  
  812.                 }  
  813.             }  
  814.         }  
  815.     }  
  816.       
  817.     /** 
  818.      * 一个简单的层次遍历B树实现,用于输出B树。 
  819.      */  
  820.     public void output()  
  821.     {  
  822.         Queue<BTreeNode<K, V>> queue = new LinkedList<BTreeNode<K, V>>();  
  823.         queue.offer(root);  
  824.         while(!queue.isEmpty())  
  825.         {  
  826.             BTreeNode<K, V> node = queue.poll();  
  827.             for(int i = 0; i < node.size(); ++ i)  
  828.                 System.out.print(node.entryAt(i) + ” “);  
  829.             System.out.println();  
  830.             if(!node.isLeaf())  
  831.             {  
  832.                 for(int i = 0; i <= node.size(); ++ i)  
  833.                     queue.offer(node.childAt(i));  
  834.             }  
  835.         }  
  836.     }  
  837.       
  838.     public static void main(String[] args)  
  839.     {  
  840.         Random random = new Random();  
  841.         BTree<Integer, Integer> btree = new BTree<Integer, Integer>(3);  
  842.         List<Integer> save = new ArrayList<Integer>();  
  843.         for(int i = 0; i < 10; ++ i)  
  844.         {  
  845.             int r = random.nextInt(100);  
  846.             save.add(r);  
  847.             System.out.println(r);  
  848.             btree.insert(r, r);  
  849.         }  
  850.           
  851.         System.out.println(“———————-“);  
  852.         btree.output();  
  853.         System.out.println(“———————-“);  
  854.         btree.delete(save.get(0));  
  855.         btree.output();  
  856.     }  
  857. }  
    原文作者:B树
    原文地址: https://blog.csdn.net/u010282707/article/details/29909087
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞