diff --git a/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java b/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java index db05cc9..220812b 100644 --- a/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java +++ b/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java @@ -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); } } diff --git a/datastructures/java/src/be/brainbaking/datastructures/trees/BTreeSearchResult.java b/datastructures/java/src/be/brainbaking/datastructures/trees/BTreeSearchResult.java index bbd00eb..d9dae83 100644 --- a/datastructures/java/src/be/brainbaking/datastructures/trees/BTreeSearchResult.java +++ b/datastructures/java/src/be/brainbaking/datastructures/trees/BTreeSearchResult.java @@ -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; } diff --git a/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java b/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java index 4b00296..6c0d4df 100644 --- a/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java +++ b/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java @@ -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 getSiblingsOf(Node child) { + List 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); + } } diff --git a/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java b/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java index ee88b8b..3e82157 100644 --- a/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java +++ b/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java @@ -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);