From 959e23575d2116efa59da76adb32f31ccfe29db9 Mon Sep 17 00:00:00 2001 From: Wouter Groeneveld Date: Wed, 4 Apr 2018 17:21:44 +0200 Subject: [PATCH] deletion btree rest --- .../datastructures/trees/BTree.java | 65 +++++++++++++++- .../datastructures/trees/Node.java | 68 ++++++++++++----- .../datastructures/trees/BTreeTest.java | 2 +- .../datastructures/trees/NodeTest.java | 76 +++++++++++++++++++ 4 files changed, 188 insertions(+), 23 deletions(-) diff --git a/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java b/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java index 220812b..fdf9d3a 100644 --- a/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java +++ b/datastructures/java/src/be/brainbaking/datastructures/trees/BTree.java @@ -1,5 +1,8 @@ package be.brainbaking.datastructures.trees; +import java.util.List; +import java.util.Optional; + public class BTree { private Node root; @@ -53,26 +56,80 @@ public class BTree { BTreeSearchResult searchResult = search(key); if(!searchResult.isFound()) return; Node node = searchResult.getNode(); + Node parent = searchResult.getParent(); 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)) { + List siblings = parent.getSiblingsOf(node); + + Optional maybeSibling = getSiblingWithTNumberOfKeys(siblings); + if(maybeSibling.isPresent()) { // 3.a - } else if(searchResult.getParent().getSiblingsOf(node).stream().allMatch(s -> s.getNumberOfKeys() == t - 1)) { + String parentKeyToPushDown = parent.getKeyBetweenChildren(node, maybeSibling.get()); + String extremeKeyToPushUp = maybeSibling.get().getExtremeKeyComparedTo(parentKeyToPushDown); + + pushKeyDown(node, parent, parentKeyToPushDown); + pullKeyUp(parent, maybeSibling, extremeKeyToPushUp); + + node.deleteKey(key); + } else if(siblings.stream().allMatch(s -> s.getNumberOfKeys() == t - 1)) { // 3.b + Node rightMostSibling = siblings.stream().filter(s -> s.getNumberOfKeys() == t - 1).reduce((one, two) -> two).get(); + + node.mergeWith(rightMostSibling); + String parentKeyToPushDown = parent.getKeyBetweenChildren(node, rightMostSibling); + + parent.getChildren().remove(rightMostSibling); + pushKeyDown(node, parent, parentKeyToPushDown); + + node.deleteKey(key); } } else if(!node.isLeaf()) { // case 2 - if(node.getLeftChild().getNumberOfKeys() >= t) { + List children = node.getChildenBetweenKey(key); + Node left = children.get(0); + Node right = children.get(1); + + if(left.getNumberOfKeys() >= t) { // 2.a - } else if(node.getRightChild().getNumberOfKeys() >= t) { + switchKeys(node, key, left, left.getLastKey()); + left.deleteKey(key); + } else if(right.getNumberOfKeys() >= t) { // 2.b + switchKeys(node, key, right, right.getFirstKey()); + right.deleteKey(key); } else { // 2.c + left.mergeWith(right); + node.getChildren().remove(right); + + node.deleteKey(key); } } } + private void switchKeys(Node node, String key, Node nodeToSwitch, String keyToSwith) { + node.getKeys().remove(key); + node.addKey(keyToSwith); + nodeToSwitch.getKeys().remove(keyToSwith); + nodeToSwitch.addKey(key); + } + + private void pullKeyUp(Node parent, Optional maybeSibling, String extremeKeyToPushUp) { + maybeSibling.get().getKeys().remove(extremeKeyToPushUp); + parent.addKey(extremeKeyToPushUp); + } + + private void pushKeyDown(Node node, Node parent, String parentKeyToPushDown) { + parent.getKeys().remove(parentKeyToPushDown); + node.addKey(parentKeyToPushDown); + } + + + private Optional getSiblingWithTNumberOfKeys(List siblings) { + return siblings.stream().filter(s -> s.getNumberOfKeys() == t).findFirst(); + } + private void insertNonFull(Node node, String key) { if(node.isLeaf()) { node.addKey(key); diff --git a/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java b/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java index 6c0d4df..8205e6c 100644 --- a/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java +++ b/datastructures/java/src/be/brainbaking/datastructures/trees/Node.java @@ -1,7 +1,10 @@ package be.brainbaking.datastructures.trees; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Each node has the following fields: @@ -35,6 +38,12 @@ public class Node { this.leaf = leaf; } + /** + * Voegt key gesorteerd toe. + * Dit heb ik enkel voor bepaalde gevallen nodig, maar gebruik ik hier gegeneraliseerd. + * Betekent een performance hit van worst-case O(n[x]) (bvb switchkeys) -> uit context af te leiden waar + * @param key + */ public void addKey(String key) { int i; for(i = 0; i < keys.size(); i++) { @@ -46,27 +55,50 @@ 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 getChildenBetweenKey(String key) { + int keyIndex = keys.indexOf(key); + return Arrays.asList(children.get(keyIndex), children.get(keyIndex + 1)); // kan volgens specs niet crashen, zie def. BTree props + } + + public String getKeyBetweenChildren(Node child1, Node child2) { + return keys.get(Math.max(children.indexOf(child1), children.indexOf(child2)) - 1); + } + + public String getExtremeKeyComparedTo(String parentKey) { + return parentKey.compareTo(keys.get(0)) < 0 ? keys.get(0) : keys.get(keys.size() - 1); } 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)); } + if(index < children.size() - 1) { + siblings.add(children.get(index + 1)); + } return siblings; } + public void mergeWith(Node node) { + if(node.getKeys().get(0).compareTo(getKeys().get(0)) >= 0) { + rightSideMergeWith(node); + } else { + leftSideMergeWith(node); + } + } + + private void leftSideMergeWith(Node node) { + keys = Stream.concat(node.keys.stream(), keys.stream()).collect(Collectors.toList()); + children = Stream.concat(node.children.stream(), children.stream()).collect(Collectors.toList()); + } + + private void rightSideMergeWith(Node node) { + keys = Stream.concat(keys.stream(), node.keys.stream()).collect(Collectors.toList()); + children = Stream.concat(children.stream(), node.children.stream()).collect(Collectors.toList()); + } + public void addChild(Node node) { addChild(children.size(), node); } @@ -85,7 +117,7 @@ public class Node { for(int i = 0; i < t - 1; i++) { newNode.addKey(keys.get(i + t)); } - // ?? Aantal sleutels en aantal children kan niet gelijk zijn? + // n[x] = aantal sleutels = size(c[x]) - 1; er zijn altijd n[x] + 1 kinderen if(!isLeaf()) { for(int i = 0; i < t; i++) { newNode.children.add(children.get(i + t - 1)); @@ -123,18 +155,18 @@ public class Node { return children; } + public String getLastKey() { + return keys.get(getNumberOfKeys() - 1); + } + + public String getFirstKey() { + return keys.get(0); + } + public List getKeys() { return keys; } - public Node getLeftChild() { - return children.get(0); - } - - public Node getRightChild() { - return children.get(children.size() - 1); - } - public boolean isLeaf() { return leaf; } diff --git a/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java b/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java index 3e82157..94e2e83 100644 --- a/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java +++ b/datastructures/java/test/be/brainbaking/datastructures/trees/BTreeTest.java @@ -36,7 +36,7 @@ public class BTreeTest { BTree tree = new BTree(root, 3); tree.delete("B"); - assertArrayEquals(Arrays.asList("A", "B").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray()); + assertArrayEquals(Arrays.asList("A", "C").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray()); } @Test diff --git a/datastructures/java/test/be/brainbaking/datastructures/trees/NodeTest.java b/datastructures/java/test/be/brainbaking/datastructures/trees/NodeTest.java index 7fc866a..329c150 100644 --- a/datastructures/java/test/be/brainbaking/datastructures/trees/NodeTest.java +++ b/datastructures/java/test/be/brainbaking/datastructures/trees/NodeTest.java @@ -10,6 +10,82 @@ import static org.junit.jupiter.api.Assertions.assertSame; public class NodeTest { + @Test + public void merge_addsKeysAndChildren() { + Node child = new Node(true); + child.addKey("I"); + + Node node = new Node(false); + node.addKey("G"); + node.addKey("H"); + node.addChild(new Node(true)); + + Node siblingToMergeWith = new Node(false); + siblingToMergeWith.addKey("K"); + siblingToMergeWith.addKey("L"); + Node siblingChild = new Node(true); + siblingChild.addKey("X"); + siblingToMergeWith.addChild(siblingChild); + + node.mergeWith(siblingToMergeWith); + + assertArrayEquals(Arrays.asList("G", "H", "K", "L").toArray(), node.getKeys().toArray()); + assertEquals(2, node.getChildren().size()); + } + + @Test + public void getKeyBetweenChildren_basedOnChildIndex() { + Node parent = new Node(false); + parent.addKey("J"); + parent.addKey("M"); + + Node child1 = new Node(true); + child1.addKey("G"); + child1.addKey("H"); + parent.addChild(child1); + Node child2 = new Node(true); + child2.addKey("K"); + child2.addKey("L"); + parent.addChild(child2); + Node child3 = new Node(true); + child3.addKey("O"); + child3.addKey("P"); + child3.addKey("R"); + parent.addChild(child3); + + assertEquals("J", parent.getKeyBetweenChildren(child1, child2)); + assertEquals("M", parent.getKeyBetweenChildren(child2, child3)); + } + + @Test + public void getExtremeKey_returnsFirstKeyBiggerThanParentKey_leftSide() { + Node parent = new Node(false); + parent.addKey("J"); + parent.addKey("M"); + + Node child = new Node(true); + child.addKey("O"); + child.addKey("P"); + child.addKey("R"); + parent.addChild(child); + + assertEquals("O", child.getExtremeKeyComparedTo("M")); + } + + @Test + public void getExtremeKey_returnsLastKeySmallerThanParentKey_rightSide() { + Node parent = new Node(false); + parent.addKey("J"); + parent.addKey("M"); + + Node child = new Node(true); + child.addKey("K"); + child.addKey("L"); + parent.addChild(child); + + assertEquals("L", child.getExtremeKeyComparedTo("M")); + } + @Test public void createFromSplitResult_nodeALeftAndBRight() { Node root = new Node(false);