blueprint deletion btrees
This commit is contained in:
parent
f29428f591
commit
01368089b1
|
@ -5,11 +5,15 @@ public class BTree {
|
||||||
private Node root;
|
private Node root;
|
||||||
private final int t;
|
private final int t;
|
||||||
|
|
||||||
public BTree(int t) {
|
public BTree(Node node, int t) {
|
||||||
this.root = Node.createRoot();
|
this.root = node;
|
||||||
this.t = t;
|
this.t = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BTree(int t) {
|
||||||
|
this(Node.createRoot(), t);
|
||||||
|
}
|
||||||
|
|
||||||
public Node getRoot() {
|
public Node getRoot() {
|
||||||
return root;
|
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) {
|
private void insertNonFull(Node node, String key) {
|
||||||
if(node.isLeaf()) {
|
if(node.isLeaf()) {
|
||||||
node.addKey(key);
|
node.addKey(key);
|
||||||
|
@ -60,19 +104,19 @@ public class BTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BTreeSearchResult search(String key) {
|
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;
|
int i = 0;
|
||||||
while(i < node.getNumberOfKeys() && key.compareTo(node.getKeys().get(i)) > 0) {
|
while(i < node.getNumberOfKeys() && key.compareTo(node.getKeys().get(i)) > 0) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if(i < node.getNumberOfKeys() && key == node.getKeys().get(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();
|
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 {
|
public class BTreeSearchResult {
|
||||||
|
|
||||||
|
private final Node parent;
|
||||||
private final Node node;
|
private final Node node;
|
||||||
private final int index;
|
private final int index;
|
||||||
|
|
||||||
public BTreeSearchResult() {
|
public BTreeSearchResult() {
|
||||||
node = null;
|
this(null, -1, null);
|
||||||
index = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFound() {
|
public boolean isFound() {
|
||||||
return node != null;
|
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.node = node;
|
||||||
|
this.parent = parent;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,27 @@ public class Node {
|
||||||
keys.add(i, key);
|
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) {
|
public void addChild(Node node) {
|
||||||
addChild(children.size(), node);
|
addChild(children.size(), node);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +127,14 @@ public class Node {
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Node getLeftChild() {
|
||||||
|
return children.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getRightChild() {
|
||||||
|
return children.get(children.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isLeaf() {
|
public boolean isLeaf() {
|
||||||
return leaf;
|
return leaf;
|
||||||
}
|
}
|
||||||
|
@ -119,4 +148,8 @@ public class Node {
|
||||||
|
|
||||||
return root;
|
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.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
// deletion examples: https://www.youtube.com/watch?v=fKubKYzwDl0 - cases omgekeerde van p451
|
||||||
public class BTreeTest {
|
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
|
@Test
|
||||||
public void searching_afterAddingExampleAndHavingToSplit() {
|
public void searching_afterAddingExampleAndHavingToSplit() {
|
||||||
BTree tree = new BTree(4);
|
BTree tree = new BTree(4);
|
||||||
|
|
Loading…
Reference in New Issue