blueprint deletion btrees
This commit is contained in:
parent
f29428f591
commit
01368089b1
|
@ -5,11 +5,15 @@ public class BTree {
|
|||
private Node root;
|
||||
private final int t;
|
||||
|
||||
public BTree(int t) {
|
||||
this.root = Node.createRoot();
|
||||
public BTree(Node node, int t) {
|
||||
this.root = node;
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public BTree(int t) {
|
||||
this(Node.createRoot(), t);
|
||||
}
|
||||
|
||||
public Node getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
@ -29,6 +33,46 @@ public class BTree {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* case 1: if x is a leaf and x has >= t keys, just delete it.
|
||||
* case 2: if x is an internal node
|
||||
* a) if x's left child has >= t keys, move the largest key to the key to delete.
|
||||
* b) if x's right child has >= t keys, move the smallest key to the key to delete.
|
||||
* c) if none of the children have >= t keys, merge the children and delete the key.
|
||||
* case 3: if x is a leaf and x has == t - 1 keys, then
|
||||
* a) if x has a sibling with at least t keys
|
||||
* - move parent's key -> x
|
||||
* - find extreme(key) sibling with at least t keys (left/right), move x's key -> parent
|
||||
* - then proceed as case 1
|
||||
* b) if x's sibling also has t - 1 keys
|
||||
* - merge x with sibling: move parent's key -> x (as t key)
|
||||
* - then delete that key
|
||||
* @param key
|
||||
*/
|
||||
public void delete(String key) {
|
||||
BTreeSearchResult searchResult = search(key);
|
||||
if(!searchResult.isFound()) return;
|
||||
Node node = searchResult.getNode();
|
||||
|
||||
if(node.isLeaf() && node.getNumberOfKeys() >= t) { // case 1
|
||||
node.deleteKey(key);
|
||||
} else if(node.isLeaf() && node.getNumberOfKeys() == t - 1) { // case 3
|
||||
if(searchResult.getParent().getSiblingsOf(node).stream().anyMatch(s -> s.getNumberOfKeys() == t)) {
|
||||
// 3.a
|
||||
} else if(searchResult.getParent().getSiblingsOf(node).stream().allMatch(s -> s.getNumberOfKeys() == t - 1)) {
|
||||
// 3.b
|
||||
}
|
||||
} else if(!node.isLeaf()) { // case 2
|
||||
if(node.getLeftChild().getNumberOfKeys() >= t) {
|
||||
// 2.a
|
||||
} else if(node.getRightChild().getNumberOfKeys() >= t) {
|
||||
// 2.b
|
||||
} else {
|
||||
// 2.c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertNonFull(Node node, String key) {
|
||||
if(node.isLeaf()) {
|
||||
node.addKey(key);
|
||||
|
@ -60,19 +104,19 @@ public class BTree {
|
|||
}
|
||||
|
||||
public BTreeSearchResult search(String key) {
|
||||
return searchInNode(root, key);
|
||||
return searchInNode(root, key, null);
|
||||
}
|
||||
|
||||
private BTreeSearchResult searchInNode(Node node, String key) {
|
||||
private BTreeSearchResult searchInNode(Node node, String key, Node parent) {
|
||||
int i = 0;
|
||||
while(i < node.getNumberOfKeys() && key.compareTo(node.getKeys().get(i)) > 0) {
|
||||
i++;
|
||||
}
|
||||
if(i < node.getNumberOfKeys() && key == node.getKeys().get(i)) {
|
||||
return new BTreeSearchResult(node, i);
|
||||
return new BTreeSearchResult(node, i, parent);
|
||||
}
|
||||
|
||||
if(node.isLeaf()) return new BTreeSearchResult();
|
||||
return searchInNode(node.getChildren().get(i), key);
|
||||
return searchInNode(node.getChildren().get(i), key, node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,25 @@ package be.brainbaking.datastructures.trees;
|
|||
|
||||
public class BTreeSearchResult {
|
||||
|
||||
private final Node parent;
|
||||
private final Node node;
|
||||
private final int index;
|
||||
|
||||
public BTreeSearchResult() {
|
||||
node = null;
|
||||
index = -1;
|
||||
this(null, -1, null);
|
||||
}
|
||||
|
||||
public boolean isFound() {
|
||||
return node != null;
|
||||
}
|
||||
|
||||
public BTreeSearchResult(Node node, int index) {
|
||||
public Node getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public BTreeSearchResult(Node node, int index, Node parent) {
|
||||
this.node = node;
|
||||
this.parent = parent;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,27 @@ public class Node {
|
|||
keys.add(i, key);
|
||||
}
|
||||
|
||||
public String getKey(Node child) {
|
||||
for(int i = 0; i < keys.size(); i++) {
|
||||
if(keys.get(i).compareTo(child.getKeys().get(0)) >= 0) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Node> getSiblingsOf(Node child) {
|
||||
List<Node> siblings = new ArrayList<>();
|
||||
int index = children.indexOf(child);
|
||||
if(index < children.size() - 1) {
|
||||
siblings.add(children.get(index + 1));
|
||||
}
|
||||
if(index > 0) {
|
||||
siblings.add(children.get(index - 1));
|
||||
}
|
||||
|
||||
return siblings;
|
||||
}
|
||||
|
||||
public void addChild(Node node) {
|
||||
addChild(children.size(), node);
|
||||
}
|
||||
|
@ -106,6 +127,14 @@ public class Node {
|
|||
return keys;
|
||||
}
|
||||
|
||||
public Node getLeftChild() {
|
||||
return children.get(0);
|
||||
}
|
||||
|
||||
public Node getRightChild() {
|
||||
return children.get(children.size() - 1);
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return leaf;
|
||||
}
|
||||
|
@ -119,4 +148,8 @@ public class Node {
|
|||
|
||||
return root;
|
||||
}
|
||||
|
||||
public void deleteKey(String key) {
|
||||
keys.remove(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,199 @@ import java.util.Arrays;
|
|||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
// deletion examples: https://www.youtube.com/watch?v=fKubKYzwDl0 - cases omgekeerde van p451
|
||||
public class BTreeTest {
|
||||
|
||||
@Test
|
||||
public void deleteCase1() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("D");
|
||||
root.addKey("G");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("A");
|
||||
child1.addKey("B");
|
||||
child1.addKey("C");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("E");
|
||||
child2.addKey("F");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("H");
|
||||
child3.addKey("I");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("B");
|
||||
|
||||
assertArrayEquals(Arrays.asList("A", "B").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteCase2A() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("Q");
|
||||
root.addKey("U");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("O");
|
||||
child1.addKey("P");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("R");
|
||||
child2.addKey("S");
|
||||
child2.addKey("T");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("W");
|
||||
child3.addKey("X");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("U");
|
||||
|
||||
assertArrayEquals(Arrays.asList("Q", "T").toArray(), tree.getRoot().getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("O", "P").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("R", "S").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("W", "X").toArray(), tree.getRoot().getChildren().get(2).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteCase2B() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("I");
|
||||
root.addKey("M");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("G");
|
||||
child1.addKey("H");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("J");
|
||||
child2.addKey("K");
|
||||
child2.addKey("L");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("O");
|
||||
child3.addKey("P");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("I");
|
||||
|
||||
assertArrayEquals(Arrays.asList("J", "M").toArray(), tree.getRoot().getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("G", "H").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("K", "L").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("O", "P").toArray(), tree.getRoot().getChildren().get(2).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteCase2C() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("R");
|
||||
root.addKey("U");
|
||||
root.addKey("X");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("P");
|
||||
child1.addKey("Q");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("S");
|
||||
child2.addKey("T");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("V");
|
||||
child3.addKey("W");
|
||||
|
||||
Node child4 = new Node(true);
|
||||
child4.addKey("Y");
|
||||
child4.addKey("Z");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
root.addChild(child4);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("U");
|
||||
|
||||
assertArrayEquals(Arrays.asList("R", "X").toArray(), tree.getRoot().getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("P", "Q").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("S", "T", "V", "W").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("Y", "Z").toArray(), tree.getRoot().getChildren().get(2).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteCase3A() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("J");
|
||||
root.addKey("M");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("G");
|
||||
child1.addKey("H");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("K");
|
||||
child2.addKey("L");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("O");
|
||||
child3.addKey("P");
|
||||
child3.addKey("R");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("L");
|
||||
|
||||
assertArrayEquals(Arrays.asList("J", "O").toArray(), tree.getRoot().getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("K", "M").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteCase3B() {
|
||||
Node root = new Node(false);
|
||||
root.addKey("Q");
|
||||
root.addKey("T");
|
||||
|
||||
Node child1 = new Node(true);
|
||||
child1.addKey("O");
|
||||
child1.addKey("P");
|
||||
|
||||
Node child2 = new Node(true);
|
||||
child2.addKey("R");
|
||||
child2.addKey("S");
|
||||
|
||||
Node child3 = new Node(true);
|
||||
child3.addKey("W");
|
||||
child3.addKey("X");
|
||||
|
||||
root.addChild(child1);
|
||||
root.addChild(child2);
|
||||
root.addChild(child3);
|
||||
|
||||
BTree tree = new BTree(root, 3);
|
||||
tree.delete("S");
|
||||
|
||||
assertArrayEquals(Arrays.asList("Q").toArray(), tree.getRoot().getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("O", "P").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
|
||||
assertArrayEquals(Arrays.asList("R", "T", "W", "X").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searching_afterAddingExampleAndHavingToSplit() {
|
||||
BTree tree = new BTree(4);
|
||||
|
|
Loading…
Reference in New Issue