BTree.javajava
package btree; /** * description: * * @author: dawn.he QQ: 905845006 * @email: dawn.he@cloudwise.com * @email: 905845006@qq.com * @date: 2019/12/12 12:03 AM */ import java.util.ArrayDeque; import java.util.LinkedList; import java.util.Queue; /** * @author Herry * * @param <K> * @param <V> */ public class BTree<K extends Comparable<K>, V> { private BTNode<K, V> mRoot = null; private long mSize = 0l; /** * @return */ public BTNode<K, V> getRootNode() { return mRoot; } /** * @return */ public long size() { return mSize; } /** * */ public void clear() { mSize = 0l; mRoot = null; } /** * @return */ private BTNode<K, V> createNode() { BTNode<K, V> btNode = new BTNode<>(); btNode.mIsLeaf = true; btNode.mCurrentKeyNum = 0; return btNode; } /** * @param key */ private void checkKey(K key) { if (key == null) { throw new IllegalArgumentException(); } } /** * 查找指定鍵所對應的值 * * @param key * @return 若鍵存在,則返回鍵所對應的值。若鍵不存在,則拋出異常。 */ public V search(K key) { checkKey(key); BTNode<K, V> currentNode = mRoot; // 迭代查找每個可能存儲key的結點 while (currentNode != null) { int possibleIdx = binarySearch(mRoot, key); BTKeyValue<K, V> possibleKeyValue = currentNode.mKeys[possibleIdx]; // 判斷二分查找返回位置索引處的元素是否爲查找的元素,如果則返回其值,如不是,則迭代到下一個可能的結點中查找 if (possibleIdx < currentNode.mCurrentKeyNum && key.compareTo(possibleKeyValue.mKey) == 0) { return possibleKeyValue.mValue; } else { currentNode = currentNode.mChildren[possibleIdx]; } } return null; } /** * 用二分查找法查找結點中鍵的位置,若找到返回鍵的位置,若沒找到,則返回鍵應該插入的位置 * * @param btNode * @param key * @return */ private int binarySearch(BTNode<K, V> btNode, K key) { BTKeyValue<K, V>[] keys = btNode.mKeys; int lo = 0; int hi = btNode.mCurrentKeyNum - 1; while (lo <= hi) { int mid = (hi - lo) / 2 + lo; int cmp = key.compareTo(keys[mid].mKey); if (cmp == 0) { return mid; } else if (cmp > 0) { lo = mid + 1; } else if (cmp < 0) { hi = mid - 1; } } return lo; } /** * 將鍵-值對插入到BTree結構中 * * @param key 鍵不容許爲null * @param value */ public void insert(K key, V value) { checkKey(key); if (mRoot == null) { mRoot = createNode(); } // 使用遞歸的方法將鍵-值對插入到BTree結構中 mRoot = insert(mRoot, key, value); } /** * 遞歸插入方法 * * @param x 要插入到的結點 * @param key * @param value * @return */ private BTNode<K, V> insert(BTNode<K, V> x, K key, V value) { // 1.首先判斷此節點是否已經爲滿,若滿,則將此節點分裂 if (x.mCurrentKeyNum == BTNode.UPPER_BOUND_KEYNUM) { x = split(x); } // 2.對沒有滿的結點進行鍵值對的查找,找出可能的鍵值對索引,和可能的鍵值對 int possibleIdx = binarySearch(x, key); /* * 因爲第一步操做會肯定當前節點爲非滿結點,故不用擔憂數組越界問題(否則試想,當此結點已滿,且要插入的鍵大於此節點中全部鍵, * 故possibleIdx的值會等於UPPER_BOUND_KEYNUM,故形成越界) */ BTKeyValue<K, V> possibleKeyValue = x.mKeys[possibleIdx]; /* * 3.判斷可能的鍵值對中的鍵是否與要插入的鍵相同(當要插入的鍵大於當前結點中全部的鍵時,possibleKeyValue取值爲x.mKeys[x. * mCurrentKeyNum]爲null,故要判斷possibleKeyValue的值是否爲空,以防止空指針異常) * 若是相同則直接替換當前值爲插入值,並返回當前結點(用於更新) */ if (possibleKeyValue != null && key.compareTo(possibleKeyValue.mKey) == 0) { possibleKeyValue.mValue = value; return x; } /* * 4.當前節點爲葉子節點時,直接插入(因爲在最前邊進行了當前結點是否爲滿的判斷,並作了相應的處理,故到此步插入鍵值對後,此節點最多爲滿,且不會溢出) * 當前結點不爲葉子結點時,遞歸到下一個可能的結點繼續尋找、插入 */ if (x.mIsLeaf) { // 4.1 for (int i = x.mCurrentKeyNum; i > possibleIdx; i--) { x.mKeys[i] = x.mKeys[i - 1]; } x.mKeys[possibleIdx] = new BTKeyValue<>(key, value); x.mCurrentKeyNum++; mSize++; } else { // 4.2 BTNode<K, V> t = insert(x.mChildren[possibleIdx], key, value); /* * 4.3判斷當返回的結點中的鍵值對數量爲1時,說明返回的結點通過了分裂,故須要將其合併到當前節點中(同上理,合併後,當前結點最多爲滿) */ if (t.mCurrentKeyNum == 1) { // 4.3.1移動當前節點中的鍵值對爲要合併的鍵值對騰出地方,並存入 for (int i = x.mCurrentKeyNum; i > possibleIdx; i--) { x.mKeys[i] = x.mKeys[i - 1]; } x.mKeys[possibleIdx] = t.mKeys[0]; // 4.3.2移動當前節點中的子結點爲要合併的子結點騰出地方,並存入 for (int i = x.mCurrentKeyNum + 1; i > possibleIdx + 1; i--) { x.mChildren[i] = x.mChildren[i - 1]; } x.mChildren[possibleIdx] = t.mChildren[0]; x.mChildren[possibleIdx + 1] = t.mChildren[1]; // 4.3.3更新當前結點的鍵值對計數器 x.mCurrentKeyNum++; } } return x; } /** * 將滿結點x分裂爲含有一個鍵值對的父結點和兩個子結點,並返回父結點的連接 * * @param x * @return */ private BTNode<K, V> split(BTNode<K, V> x) { int mid = x.mCurrentKeyNum / 2; BTNode<K, V> left = new BTNode<>(); for (int i = 0; i < mid; i++) { left.mKeys[i] = x.mKeys[i]; left.mChildren[i] = x.mChildren[i]; } left.mChildren[mid] = x.mChildren[mid]; left.mIsLeaf = x.mIsLeaf; left.mCurrentKeyNum = mid; BTNode<K, V> right = new BTNode<>(); for (int i = mid + 1; i < x.mCurrentKeyNum; i++) { right.mKeys[i - mid - 1] = x.mKeys[i]; right.mChildren[i - mid - 1] = x.mChildren[i]; } right.mChildren[x.mCurrentKeyNum - mid - 1] = x.mChildren[x.mCurrentKeyNum]; right.mIsLeaf = x.mIsLeaf; right.mCurrentKeyNum = x.mCurrentKeyNum - mid - 1; BTNode<K, V> top = new BTNode<>(); top.mCurrentKeyNum = 1; top.mIsLeaf = false; top.mKeys[0] = x.mKeys[mid]; top.mChildren[0] = left; top.mChildren[1] = right; return top; } /** * @return */ public BTKeyValue<K, V> minKey() { return minKey(mRoot); } /** * @param x * @return */ private BTKeyValue<K, V> minKey(BTNode<K, V> x) { if (x == null) { return null; } if (x.mChildren[0] != null) { return minKey(x.mChildren[0]); } else { return x.mKeys[0]; } } /** * @return */ public BTKeyValue<K, V> maxKey() { return maxKey(mRoot); } /** * @param x * @return */ private BTKeyValue<K, V> maxKey(BTNode<K, V> x) { if (x == null) { return null; } if (x.mChildren[x.mCurrentKeyNum] != null) { return maxKey(x.mChildren[x.mCurrentKeyNum]); } else { return x.mKeys[x.mCurrentKeyNum - 1]; } } /** * * @param key * @return */ public V deleteKey(K key) { checkKey(key); V v = search(key); // 遞歸的刪除鍵key mRoot = deleteKey(mRoot, key); return v; } /** * @param x * @param key * @return */ private BTNode<K, V> deleteKey(BTNode<K, V> x, K key) { // 1.獲取要刪除的鍵可能處在當前結點上的索引位置 int possibleIdx = binarySearch(x, key); // 2.根據當前結點是否爲葉子結點分狀況討論 if (x.mIsLeaf == true) { // 2.1當前結點爲葉子節點 if (possibleIdx < x.mCurrentKeyNum && key.compareTo(x.mKeys[possibleIdx].mKey) == 0) { // 2.1.1判斷在當前結點上possible索引位置上的鍵是否與要刪除的鍵相等(前提是possible索引小於當前節點鍵的數量,負責會出現空指針異常) // 若是相等,則直接刪除此鍵,不然,此鍵不存在樹中,不作任何操做 for (int i = possibleIdx; i < x.mCurrentKeyNum - 1; i++) { x.mKeys[i] = x.mKeys[i + 1]; } x.mKeys[x.mCurrentKeyNum - 1] = null; x.mCurrentKeyNum--; mSize--; } } else { // 2.2當前結點不爲子結點 if (possibleIdx < x.mCurrentKeyNum && key.compareTo(x.mKeys[possibleIdx].mKey) == 0) { // 2.2.1判斷在當前結點上possible索引位置上的鍵是否與要刪除的鍵相等(前提是possible索引小於當前節點鍵的數量,負責會出現空指針異常) // 若是成立,用possible索引處的子結點的最大鍵替換要刪除的鍵 // 1)記住possilbe索引處子結點的最大鍵 BTKeyValue<K, V> keyNeedToSwim = maxKey(x.mChildren[possibleIdx]); // 2)將1)中記住的鍵刪除 x = deleteKey(x, keyNeedToSwim.mKey); // 3)將key替換爲記住的鍵 possibleIdx = binarySearch(x, key); x.mKeys[possibleIdx] = keyNeedToSwim; } else { // 2.2.2 // 若是不成立,遞歸的在possible索引處子結點上刪除鍵key // 遞歸刪除 BTNode<K, V> t = deleteKey(x.mChildren[possibleIdx], key); // 檢測刪除後返回結點的狀態,若是不知足鍵數量>=最低度數-1,則酌情旋轉或合併 if (t.mCurrentKeyNum < BTNode.LOWER_BOUND_KEYNUM) { // 不知足鍵數量>=最低度數-1 // 判斷返回結點的兄弟結點的情況,若兄弟結點的鍵數量>最低度數-1,則旋轉(向兄弟結點借鍵),若兄弟結點的鍵數量<=最低度數-1,則與兄弟結點合併 if (BTNode.hasLeftSiblingAtIndex(x, possibleIdx)) { if (BTNode.getLeftSiblingAtIndex(x, possibleIdx).mCurrentKeyNum > BTNode.LOWER_BOUND_KEYNUM) { leftRotate(x, possibleIdx); } else { leftMerge(x, possibleIdx); } } else if (BTNode.hasRightSiblingAtIndex(x, possibleIdx)) { if (BTNode.getRightSiblingAtIndex(x, possibleIdx).mCurrentKeyNum > BTNode.LOWER_BOUND_KEYNUM) { rightRotate(x, possibleIdx); } else { rightMerge(x, possibleIdx); } } // 判斷刪完後根節點是否沒有鍵存在,若沒有,則將根節點從新賦值 if (x == mRoot && x.mCurrentKeyNum == 0) { x = x.mChildren[0]; } } } } return x; } /** * 合併父結點中位於possibleIdx和possibleIdx+1處的倆結點(因而可知可用執行作合併來完成右合併一樣的任務) * * @param x * @param possibleIdx * @return */ private BTNode<K, V> rightMerge(BTNode<K, V> x, int possibleIdx) { return leftMerge(x, possibleIdx + 1); } /** * 合併父結點中位於possibleIdx和possibleIdx-1處的倆結點 * * @param x * @param possibleIdx * @return */ private BTNode<K, V> leftMerge(BTNode<K, V> x, int possibleIdx) { // 獲取左節點 BTNode<K, V> leftNode = x.mChildren[possibleIdx - 1]; // 獲取父結點要合併到左節點的鍵值對 BTKeyValue<K, V> topKey = x.mKeys[possibleIdx - 1]; // 獲取須要合併的結點 BTNode<K, V> possibleNode = x.mChildren[possibleIdx]; // 將父結點獲取的鍵值對放入左節點 leftNode.mKeys[leftNode.mCurrentKeyNum] = topKey; // 將須要合併的結點的鍵值對所有放入左節點 for (int i = 0; i < possibleNode.mCurrentKeyNum; i++) { leftNode.mKeys[i + leftNode.mCurrentKeyNum + 1] = possibleNode.mKeys[i]; } // 同理,將結點連接也移過來 for (int i = 0; i < possibleNode.mCurrentKeyNum + 1; i++) { leftNode.mChildren[i + leftNode.mCurrentKeyNum + 1] = possibleNode.mChildren[i]; } // 更新左節點的鍵值對計數器 leftNode.mCurrentKeyNum += 1 + possibleNode.mCurrentKeyNum; // 更新父結點 for (int i = possibleIdx; i < x.mCurrentKeyNum; i++) { x.mKeys[i - 1] = x.mKeys[i]; } x.mKeys[x.mCurrentKeyNum - 1] = null; for (int i = possibleIdx; i < x.mCurrentKeyNum; i++) { x.mChildren[i] = x.mChildren[i + 1]; } x.mChildren[x.mCurrentKeyNum] = null; x.mCurrentKeyNum--; // System.out.println("leftMerge executed"); return x; } /** * 從右結點借一個鍵值對過來 * * @param x * @param possibleIdx * @return */ private BTNode<K, V> rightRotate(BTNode<K, V> x, int possibleIdx) { // 獲取右節點和右節點中最小的鍵值對 BTNode<K, V> rightNode = x.mChildren[possibleIdx + 1]; BTKeyValue<K, V> rightKey = rightNode.mKeys[0]; // 獲取右節點中最小的結點 BTNode<K, V> rightFirstNode = rightNode.mChildren[0]; // 獲取父結點交換位置的鍵值對 BTKeyValue<K, V> topKey = x.mKeys[possibleIdx]; // 獲取需補齊鍵值對的節點,並將父結點交換位置的鍵值對加到此節點的最高位 BTNode<K, V> possibleNode = x.mChildren[possibleIdx]; possibleNode.mKeys[possibleNode.mCurrentKeyNum] = topKey; // 將右節點中最小的結點添加到此節點 possibleNode.mChildren[possibleNode.mCurrentKeyNum + 1] = rightFirstNode; possibleNode.mCurrentKeyNum++; // 將父結點拿走鍵值對的位置填上右節點提出的鍵值對 x.mKeys[possibleIdx] = rightKey; // 將右節點提出的鍵值對和最小結點在右節點中刪除 for (int i = 1; i < rightNode.mCurrentKeyNum; i++) { rightNode.mKeys[i - 1] = rightNode.mKeys[i]; } rightNode.mKeys[rightNode.mCurrentKeyNum - 1] = null; for (int i = 1; i < rightNode.mCurrentKeyNum + 1; i++) { rightNode.mChildren[i - 1] = rightNode.mChildren[i]; } rightNode.mChildren[rightNode.mCurrentKeyNum] = null; rightNode.mCurrentKeyNum--; // System.out.println("rightRotate executed"); return x; } /** * ‘ * * @param x 父結點 * @param possibleIdx 須要補充鍵值對的子結點的索引 * @return */ private BTNode<K, V> leftRotate(BTNode<K, V> x, int possibleIdx) { // 獲取左節點和左節點中最大的鍵值對 BTNode<K, V> leftNode = x.mChildren[possibleIdx - 1]; BTKeyValue<K, V> leftKey = leftNode.mKeys[leftNode.mCurrentKeyNum - 1]; // 獲取左節點中最大的結點 BTNode<K, V> leftLastNode = leftNode.mChildren[leftNode.mCurrentKeyNum]; // 獲取父結點交換位置的鍵值對 BTKeyValue<K, V> topKey = x.mKeys[possibleIdx - 1]; // 獲取需補齊鍵值對的節點,並移動其中的鍵值對將最低位空出來:以用來填充從父結點交換過來的鍵值對 BTNode<K, V> possibleNode = x.mChildren[possibleIdx]; for (int i = possibleNode.mCurrentKeyNum; i > 0; i--) { possibleNode.mKeys[i] = possibleNode.mKeys[i - 1]; } // 同理對此節點的子結點 for (int i = possibleNode.mCurrentKeyNum + 1; i > 0; i--) { possibleNode.mChildren[i] = possibleNode.mChildren[i - 1]; } // 填充鍵值對和其帶過來的連接,並將鍵數量計數器加1 possibleNode.mKeys[0] = topKey; possibleNode.mChildren[0] = leftLastNode; possibleNode.mCurrentKeyNum++; // 將父結點拿走鍵值對的位置填上左節點提出的鍵值對 x.mKeys[possibleIdx - 1] = leftKey; // 將左節點提出的鍵值對和子結點在左節點中刪除 leftNode.mKeys[leftNode.mCurrentKeyNum - 1] = null; leftNode.mChildren[leftNode.mCurrentKeyNum] = null; leftNode.mCurrentKeyNum--; // System.out.println("leftRotate executed"); return x; } public static void main(String[] args) { BTree<Integer, String> bt = new BTree<>(); for (int i = 1; i <= 56; i++) { bt.insert(i, ""); } System.out.println("insert completed"); System.out.println("size before delete:" + bt.size()); bt.deleteKey(27); bt.deleteKey(42); System.out.println("size after delete:" + bt.size()); Queue<BTNode<Integer, String>> queue = new ArrayDeque<>(); ((ArrayDeque<BTNode<Integer,String>>) queue).offerLast(bt.getRootNode()); while (!queue.isEmpty()) { BTNode<Integer, String> btn = queue.poll(); for (int i = 0; i < btn.mCurrentKeyNum; i++) { System.out.print(btn.mKeys[i].mKey + " "); } System.out.println(); if (!btn.mIsLeaf) { for (int i = 0; i <= btn.mCurrentKeyNum; i++) { ((ArrayDeque<BTNode<Integer,String>>) queue).offerLast(btn.mChildren[i]); } } } // //add()和remove()方法在失敗的時候會拋出異常(不推薦) // Queue<String> queue = new ArrayDeque<String>(); // //添加元素 // queue.offer("a"); // queue.offer("b"); // queue.offer("c"); // queue.offer("d"); // queue.offer("e"); // for(String q : queue){ // System.out.println(q); // } // System.out.println("==="); // System.out.println("poll="+queue.poll()); //返回第一個元素,並在隊列中刪除 // for(String q : queue){ // System.out.println(q); // } // System.out.println("==="); // System.out.println("element="+queue.element()); //返回第一個元素 // for(String q : queue){ // System.out.println(q); // } // System.out.println("==="); // System.out.println("peek="+queue.peek()); //返回第一個元素 // for(String q : queue){ // System.out.println(q); // } } } /** * * * * |9|26|45| * |p1|p2|p3|p4| * * | | | | * * * | | | | * * * * | | | | * * | | | | * * | | | | * * | | | | * * |3 | 6| |12|15|18|21| |30|33|36|41| |3 | 6| * |p1|p2|p3| |p1|p2|p3|p4|p5| |p1|p2|p3|p4|p5| |p1|p2|p3| * | | | | | | | | | | | | | | | | * * * | | | | | | | | | | | | | | | | * * | | | | | | | * * | | | | | | | | | | | | | | | | * * * | | | | | | | | | | | | | | | | * * | * | | | | | | | | | | | | | | | | * * | | | | | | | | | | | | | | | | * *|1 | 2| |4 | 5| |7 | 8| |10|11| |13|14| |16|17| |19|20| |22|23|24|25| |28|29| |31|32| |34|35| |37|38|39|40| |43|44| |46|47| |49|50| |52|53|54|55|56| * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3|p4|p5| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3|p4|p5| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3| |p1|p2|p3|p4|p5 * * */
BTNode.java數組
package btree; /** * description: * * @author: dawn.he QQ: 905845006 * @email: dawn.he@cloudwise.com * @email: 905845006@qq.com * @date: 2019/12/12 12:04 AM */ public class BTNode<K extends Comparable<K>, V> { // 構成B樹的最小度數 public final static int MIN_DEGREE = 3; // 除根節點外,每一個結點中總鍵數的下限 public final static int LOWER_BOUND_KEYNUM = MIN_DEGREE - 1; // 包含根節點外,每一個結點中總鍵數的上限 public final static int UPPER_BOUND_KEYNUM = (MIN_DEGREE * 2) - 1; protected boolean mIsLeaf;// 標記此節點是否爲葉子結點 protected int mCurrentKeyNum;// 此節點的鍵數量計數器 protected BTKeyValue<K, V>[] mKeys;// 用於存鍵值對的數組 protected BTNode<K, V>[] mChildren;// 用於存子結點的數組 /** * 構造函數 */ @SuppressWarnings("unchecked") public BTNode() { mIsLeaf = true; mCurrentKeyNum = 0; mKeys = new BTKeyValue[UPPER_BOUND_KEYNUM]; mChildren = new BTNode[UPPER_BOUND_KEYNUM + 1]; } protected static BTNode<?, ?> getChildNodeAtIndex(BTNode<?, ?> btNode, int keyIdx, int nDirection) { if (btNode.mIsLeaf) { return null; } keyIdx += nDirection; if ((keyIdx < 0) || (keyIdx > btNode.mCurrentKeyNum)) { throw new IllegalArgumentException(); } return btNode.mChildren[keyIdx]; } /** * 返回btNode節點中位於keyIdx位上的鍵左邊的子結點 * @param btNode * @param keyIdx * @return */ protected static BTNode<?, ?> getLeftChildAtIndex(BTNode<?, ?> btNode, int keyIdx) { return getChildNodeAtIndex(btNode, keyIdx, 0); } /** * 返回btNode節點中位於keyIdx位上的鍵右邊的子結點 * @param btNode * @param keyIdx * @return */ protected static BTNode<?, ?> getRightChildAtIndex(BTNode<?, ?> btNode, int keyIdx) { return getChildNodeAtIndex(btNode, keyIdx, 1); } /** * @param parentNode * @param keyIdx * @return 返回父結點的keyIdx位上的子結點的左兄弟結點 */ protected static BTNode<?, ?> getLeftSiblingAtIndex(BTNode<?, ?> parentNode, int keyIdx) { return getChildNodeAtIndex(parentNode, keyIdx, -1); } /** * * @param parentNode * @param keyIdx * @return 返回父結點的keyIdx位上的子結點的右兄弟結點 */ protected static BTNode<?, ?> getRightSiblingAtIndex(BTNode<?, ?> parentNode, int keyIdx) { return getChildNodeAtIndex(parentNode, keyIdx, 1); } /** * 判斷父結點的keyIdx位上的子結點是否存在左兄弟結點 * @param parentNode * @param keyIdx * @return */ protected static boolean hasLeftSiblingAtIndex(BTNode<?, ?> parentNode, int keyIdx) { if (keyIdx - 1 < 0) { return false; } else { return true; } } /** * 判斷父結點的keyIdx位上的子結點是否存在右兄弟結點 * @param parentNode * @param keyIdx * @return */ protected static boolean hasRightSiblingAtIndex(BTNode<?, ?> parentNode, int keyIdx) { if (keyIdx + 1 > parentNode.mCurrentKeyNum) { return false; } else { return true; } } }
BTKeyValue.java函數
package btree; /** * description: * * @author: dawn.he QQ: 905845006 * @email: dawn.he@cloudwise.com * @email: 905845006@qq.com * @date: 2019/12/12 12:04 AM */ /** * @author Herry * * @param <K> * @param <V> */ public class BTKeyValue<K extends Comparable<K>, V> { protected K mKey; protected V mValue; public BTKeyValue(K mKey, V mValue) { super(); this.mKey = mKey; this.mValue = mValue; } }