【BTree】
Definition of BTree
GPT:
B-tree (B-tree) is a self-balancing tree data structure, originally proposed by Rudolf Bayer and Edward M. McCreight in 1972. It is usually used to achieve efficient data organization and retrieval in databases and file systems. The name “B” of the B-tree stands for “Bayer” (one of the inventors).
B-trees have the following main characteristics:1. Balance: B-tree is a balanced tree, which means that the difference between the longest path and the shortest path from the root node to the leaf node will not be too big. This ensures that the depth of each branch is relatively balanced, providing a predictable retrieval performance.
2. Multi-way search tree: Each node can contain multiple child nodes, usually more than two child nodes, which makes the B-tree a multi-way search tree. This helps reduce the height of the tree, thereby reducing the complexity of find, insert, and delete operations.
3. Orderliness: The nodes in the B-tree are usually arranged in ascending order. This ordering facilitates range query and range scan operations because related data is often stored adjacent to each other in a B-tree.
4. Hierarchical storage: B-trees are often used for data stored on disks or other hierarchical storage devices because its structure allows efficient disk I/O. The node size of a B-tree is usually set to the size of a disk block to minimize I/O operations.
5. Self-balancing: When a node is inserted or deleted, the B-tree will automatically perform a balancing operation to ensure that the balance of the tree is maintained. This is achieved by redistributing keys and nodes and merging or splitting nodes.B-trees are widely used as index structures in database management systems. For example, B+ trees are used for database indexes, file system management, and other applications that require efficient insertion, deletion, and search operations. Their balanced and multipath nature make them very useful in large-scale data storage and retrieval.
Code implementation
package DataStruction; import java.util.Arrays; /** * Degree concept: Degree refers to the upper limit or maximum capacity of the number of child nodes of each node in the tree */ public class BTree {<!-- --> //B tree implementation static class Node {<!-- --> int[] keys; //Value array of this node Node[] children;//array of child nodes int keyNumbers;//The length of the valid value in the array boolean leaf = true;//Is it a leaf node? int t;//degree, represents the number of children public Node(int t) {<!-- --> this.t = t; children = new Node[2 * t]; //Maximum number of children keys = new int[2 * t - 1]; //Maximum number of values, } @Override public String toString() {<!-- --> return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumbers)); } Node get(int key) {<!-- --> int i = 0; while (i < keyNumbers) {<!-- --> if (keys[i] == key) {<!-- --> return this; } if (keys[i] > key) {<!-- --> break; } i + + ; } if (leaf) {<!-- --> return null; } return children[i].get(key); } void insertKey(int key, int index) {<!-- --> if (index >= 0 & amp; & amp; index < keyNumbers & amp; & amp; keyNumbers < 2 * t - 1) {<!-- --> //Move array elements to make room for insertion System.arraycopy(keys, index, keys, index + 1, keyNumbers - index); //Insert new key keys[index] = key; keyNumbers + + ; } else {<!-- --> System.out.println("Invalid index for key insertion."); } } void insertChild(Node child, int index) {<!-- --> if (index >= 0 & amp; & amp; index <= t & amp; & amp; keyNumbers < 2 * t - 1) {<!-- --> //Move array elements to make room for insertion System.arraycopy(children, index, children, index + 1, keyNumbers - index + 1); //Insert new child children[index] = child; } else {<!-- --> System.out.println("Invalid index for child insertion."); } } /** * Delete the key at the specified location from the current node * @param index The location of the key to be deleted */ int deleteKey(int index) {<!-- --> int ans = keys[index]; if (index >= 0 & amp; & amp; index < keyNumbers) {<!-- --> //Move the array element to cover the deleted position System.arraycopy(keys, index + 1, keys, index, keyNumbers - index - 1); keyNumbers--; } else {<!-- --> System.out.println("Invalid index for key deletion."); } return ans; } /** * Delete the leftmost key of the current node */ int deleteLeftmostKey() {<!-- --> return deleteKey(0); } /** * Delete the rightmost key of the current node */ int deleteRightmostKey() {<!-- --> return deleteKey(keyNumbers - 1); } /** * Delete the child at the specified position of the current node * @param index The position of the child to be deleted */ Node deleteChild(int index) {<!-- --> Node deleted = children[index]; if (index >= 0 & amp; & amp; index < keyNumbers + 1) {<!-- --> //Move the array element to cover the deleted position System.arraycopy(children, index + 1, children, index, keyNumbers - index); children[keyNumbers] = null; // Set the rightmost child to null } else {<!-- --> System.out.println("Invalid index for child deletion."); } return deleted; } /** * Delete the leftmost child of the current node */ Node deleteLeftmostChild() {<!-- --> return deleteChild(0); } /** * Delete the rightmost child of the current node */ Node deleteRightmostChild() {<!-- --> return deleteChild(keyNumbers); } /** * Copy all keys and children of the current node to the target node * @param target target node */ void copyTo(Node target) {<!-- --> //Copy keys array System.arraycopy(this.keys, 0, target.keys, 0, this.keyNumbers); //Copy children array System.arraycopy(this.children, 0, target.children, 0, this.keyNumbers + 1); //Update the keyNumbers of the target node target.keyNumbers = this.keyNumbers; } /** * * @param index The left brother of the child at index * @return left sibling node */ Node childLeftSibling(int index){<!-- --> return index==0?null:children[index-1]; } /** * * @param index The right brother of the child at index * @return right sibling node */ Node childRightSibling(int index){<!-- --> return index==keyNumbers-1?null:children[index + 1]; } } Node root;//root node int t;//minimum degree int MIN_KEY_NUMBER;//Minimum number of keys int MAX_KEY_NUMBER;//Maximum number of keys public BTree(int t) {<!-- --> this.t = t; root = new Node(t); MIN_KEY_NUMBER = t - 1;//Each node can have at least t - 1 keywords. MAX_KEY_NUMBER = 2 * t - 1;//Each node can contain up to 2 * t - 1 keywords (or data items). This is because each keyword needs to be separated by at least one child node. } public BTree() {<!-- --> this(2);//Default is 2 } //Include public boolean contains(int key) {<!-- --> return root.get(key) != null; } //Add new void put(int key){<!-- --> // doPut(root,key,null,0); } void doPut(Node node,int key,Node parent,int index){<!-- --> int i = 0; while(i<node.keyNumbers){<!-- --> if (node.keys[i]==key){<!-- --> //This value already exists, update it, of course in the form of key-value pairs, values are omitted here for simplicity return; } if (node.keys[i]>key){<!-- --> break; } i + + ; } // Found a value larger than it. If it is a leaf node, just insert it directly into the node. if (node.leaf){<!-- --> node.insertKey(key,i); }else {<!-- --> //If it is not a leaf node, should we continue looking down? doPut(node.children[i],key,node,i); } //After the new addition, does each node here need to be updated, and is the maximum t in the keys full? need to split if (node.keyNumbers==MAX_KEY_NUMBER){<!-- --> split(node,parent,index); } } void split(Node left,Node parent,int index){<!-- --> if (parent==null){<!-- --> //The root node is split, and a new root node needs to be created. Node newRoot = new Node(t); newRoot.leaf = false; newRoot.insertChild(left,0); this.root = newRoot; parent = newRoot; } //Split //1. Create a right node, move the key after t to the right node and leave //2. Move the key at t-1 to the parent node index //3. Use right as the rightmost child index + 1 of parent Node right = new Node(t); right.leaf = left.leaf; //1 System.arraycopy(left.keys,t,right.keys,0,t-1); if (!left.leaf){<!-- --> //It is not a leaf node, you need to divide the children there as well. System.arraycopy(left.children,t,right.children,0,t); } //Set the effective length right.keyNumbers = t-1; //2 parent.insertKey(left.deleteKey(t-1),index); left.keyNumbers = t-1; //3 parent.insertChild(right,index + 1); } void doSplit(){<!-- --> //Recursive implementation } void remove(int key){<!-- --> doRemove(null,root,key,0); } void doRemove(Node parent,Node node,int key,int index){<!-- --> int i = 0; while(i<node.keyNumbers){<!-- --> if (node.keys[i]>=key){<!-- --> //turn up break; } i + + ; } //Currently a leaf node if (node.leaf){<!-- --> if (found(node, key, i)){<!-- --> //case 2: found, delete directly node.deleteKey(i); }else {<!-- --> //case 1: not found return; } }else {<!-- --> if (found(node,key,i)){<!-- --> //case 4: non-leaf node, found //Idea, replace the successor node Node p = node.children[i + 1]; while(!p.leaf){<!-- --> p = p.children[0]; } //This is the node where the successor value is located node.keys[i] = p.keys[0]; doRemove(node,node.children[i + 1],p.keys[0],i + 1); }else {<!-- --> //case 3: non-leaf node, not found, go to the child i node to find it doRemove(node,node.children[i],key,i); } } //After deletion, you need to consider whether it is less than the minimum degree and needs balance adjustment? if (node.keyNumbers<MIN_KEY_NUMBER){<!-- --> //balance adjustment balance(parent,node,index); } } /** * * @param parent the parent node to be adjusted * @param x Adjustment node * @param The index where the i value is located */ void balance(Node parent,Node x,int i){<!-- --> //case 6: root node //case 5-1: The brother on the left is rich, and the brother on the right is right //case 5-2: The brother on the right rotates left //case 5-3: Neither is rich, merge if (x==root){<!-- --> //Adjust the root node so that the value of the root node is assigned to the current node if (root.keyNumbers==0 & amp; & amp;root.children[0]!=null){<!-- --> root = root.children[0]; } return; } //Adjust the left and right children of the node Node left = parent.childLeftSibling(i); Node right = parent.childRightSibling(i); //5-1 if (left!=null & amp; & amp;left.keyNumbers>MIN_KEY_NUMBER){<!-- --> //right rotation x.insertKey(parent.keys[i-1],0);//Get the predecessor key of the parent node if (!left.leaf){<!-- --> //If there are children, if the key is reduced, the children will also be reduced and given to the node with the new key. x.insertChild(left.deleteRightmostChild(),0); } parent.keys[i-1] = left.deleteRightmostKey();//Get the rightmost key of the rich left brother to parent return; } if (right!=null & amp; & amp;right.keyNumbers>MIN_KEY_NUMBER){<!-- --> //left-hand rotation x.insertKey(parent.keys[i],x.keyNumbers);//Unscrew the successor node of the current node in the parent node if (!right.leaf){<!-- --> //There is a child on the right x.insertChild(right.deleteLeftmostChild(),x.keyNumbers + 1); } parent.keys[i] = right.deleteLeftmostKey();//Give the rich right brother’s leftmost key to his father return; } //Both sides are not enough to borrow, merge: divided into those with left brothers and those without left brothers parent.deleteChild(i); if (left!=null){<!-- --> //There is a left child //a. Delete the currently unadjusted node from the parent node //b. Merge to the left brother and move the successor node of the parent node to the left brother. left.insertKey(parent.deleteKey(i-1), left.keyNumbers); //c. Copy the current node to the left brother x.copyTo(left); }else {<!-- --> //No, merge to yourself x.insertKey(parent.deleteKey(i),x.keyNumbers); right.copyTo(x); } } private static boolean found(Node node, int key, int i) {<!-- --> return i < node.keyNumbers & amp; & amp; node.keys[i] == key; } }