deletion btree rest
This commit is contained in:
parent
01368089b1
commit
959e23575d
|
@ -1,5 +1,8 @@
|
||||||
package be.brainbaking.datastructures.trees;
|
package be.brainbaking.datastructures.trees;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class BTree {
|
public class BTree {
|
||||||
|
|
||||||
private Node root;
|
private Node root;
|
||||||
|
@ -53,26 +56,80 @@ public class BTree {
|
||||||
BTreeSearchResult searchResult = search(key);
|
BTreeSearchResult searchResult = search(key);
|
||||||
if(!searchResult.isFound()) return;
|
if(!searchResult.isFound()) return;
|
||||||
Node node = searchResult.getNode();
|
Node node = searchResult.getNode();
|
||||||
|
Node parent = searchResult.getParent();
|
||||||
|
|
||||||
if(node.isLeaf() && node.getNumberOfKeys() >= t) { // case 1
|
if(node.isLeaf() && node.getNumberOfKeys() >= t) { // case 1
|
||||||
node.deleteKey(key);
|
node.deleteKey(key);
|
||||||
} else if(node.isLeaf() && node.getNumberOfKeys() == t - 1) { // case 3
|
} else if(node.isLeaf() && node.getNumberOfKeys() == t - 1) { // case 3
|
||||||
if(searchResult.getParent().getSiblingsOf(node).stream().anyMatch(s -> s.getNumberOfKeys() == t)) {
|
List<Node> siblings = parent.getSiblingsOf(node);
|
||||||
|
|
||||||
|
Optional<Node> maybeSibling = getSiblingWithTNumberOfKeys(siblings);
|
||||||
|
if(maybeSibling.isPresent()) {
|
||||||
// 3.a
|
// 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
|
// 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
|
} else if(!node.isLeaf()) { // case 2
|
||||||
if(node.getLeftChild().getNumberOfKeys() >= t) {
|
List<Node> children = node.getChildenBetweenKey(key);
|
||||||
|
Node left = children.get(0);
|
||||||
|
Node right = children.get(1);
|
||||||
|
|
||||||
|
if(left.getNumberOfKeys() >= t) {
|
||||||
// 2.a
|
// 2.a
|
||||||
} else if(node.getRightChild().getNumberOfKeys() >= t) {
|
switchKeys(node, key, left, left.getLastKey());
|
||||||
|
left.deleteKey(key);
|
||||||
|
} else if(right.getNumberOfKeys() >= t) {
|
||||||
// 2.b
|
// 2.b
|
||||||
|
switchKeys(node, key, right, right.getFirstKey());
|
||||||
|
right.deleteKey(key);
|
||||||
} else {
|
} else {
|
||||||
// 2.c
|
// 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<Node> 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<Node> getSiblingWithTNumberOfKeys(List<Node> siblings) {
|
||||||
|
return siblings.stream().filter(s -> s.getNumberOfKeys() == t).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package be.brainbaking.datastructures.trees;
|
package be.brainbaking.datastructures.trees;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each node has the following fields:
|
* Each node has the following fields:
|
||||||
|
@ -35,6 +38,12 @@ public class Node {
|
||||||
this.leaf = leaf;
|
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) {
|
public void addKey(String key) {
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < keys.size(); i++) {
|
for(i = 0; i < keys.size(); i++) {
|
||||||
|
@ -46,27 +55,50 @@ public class Node {
|
||||||
keys.add(i, key);
|
keys.add(i, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey(Node child) {
|
public List<Node> getChildenBetweenKey(String key) {
|
||||||
for(int i = 0; i < keys.size(); i++) {
|
int keyIndex = keys.indexOf(key);
|
||||||
if(keys.get(i).compareTo(child.getKeys().get(0)) >= 0) {
|
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<Node> getSiblingsOf(Node child) {
|
public List<Node> getSiblingsOf(Node child) {
|
||||||
List<Node> siblings = new ArrayList<>();
|
List<Node> siblings = new ArrayList<>();
|
||||||
int index = children.indexOf(child);
|
int index = children.indexOf(child);
|
||||||
if(index < children.size() - 1) {
|
|
||||||
siblings.add(children.get(index + 1));
|
|
||||||
}
|
|
||||||
if(index > 0) {
|
if(index > 0) {
|
||||||
siblings.add(children.get(index - 1));
|
siblings.add(children.get(index - 1));
|
||||||
}
|
}
|
||||||
|
if(index < children.size() - 1) {
|
||||||
|
siblings.add(children.get(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
return siblings;
|
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) {
|
public void addChild(Node node) {
|
||||||
addChild(children.size(), node);
|
addChild(children.size(), node);
|
||||||
}
|
}
|
||||||
|
@ -85,7 +117,7 @@ public class Node {
|
||||||
for(int i = 0; i < t - 1; i++) {
|
for(int i = 0; i < t - 1; i++) {
|
||||||
newNode.addKey(keys.get(i + t));
|
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()) {
|
if(!isLeaf()) {
|
||||||
for(int i = 0; i < t; i++) {
|
for(int i = 0; i < t; i++) {
|
||||||
newNode.children.add(children.get(i + t - 1));
|
newNode.children.add(children.get(i + t - 1));
|
||||||
|
@ -123,18 +155,18 @@ public class Node {
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLastKey() {
|
||||||
|
return keys.get(getNumberOfKeys() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstKey() {
|
||||||
|
return keys.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getKeys() {
|
public List<String> getKeys() {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class BTreeTest {
|
||||||
BTree tree = new BTree(root, 3);
|
BTree tree = new BTree(root, 3);
|
||||||
tree.delete("B");
|
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
|
@Test
|
||||||
|
|
|
@ -10,6 +10,82 @@ import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
|
||||||
public class NodeTest {
|
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
|
@Test
|
||||||
public void createFromSplitResult_nodeALeftAndBRight() {
|
public void createFromSplitResult_nodeALeftAndBRight() {
|
||||||
Node root = new Node(false);
|
Node root = new Node(false);
|
||||||
|
|
Loading…
Reference in New Issue