btrees hf18

This commit is contained in:
wgroeneveld 2018-04-03 15:50:57 +02:00
parent bee1b816cd
commit f29428f591
6 changed files with 390 additions and 0 deletions

View File

@ -0,0 +1,78 @@
package be.brainbaking.datastructures.trees;
public class BTree {
private Node root;
private final int t;
public BTree(int t) {
this.root = Node.createRoot();
this.t = t;
}
public Node getRoot() {
return root;
}
public void add(String key) {
Node node = root;
if(node.isFull(t)) {
NodeSplitResult splitResult = node.split(t);
Node newNode = Node.createFromSplitResult(node, splitResult);
root = newNode;
insertNonFull(newNode, key);
} else {
insertNonFull(node, key);
}
}
private void insertNonFull(Node node, String key) {
if(node.isLeaf()) {
node.addKey(key);
} else {
int i = findRightChildIndexToSearchThrough(node, key);
Node nodeToSeekThrough = node.getChildren().get(i - 1);
if(nodeToSeekThrough.isFull(t)) {
NodeSplitResult splitResult = nodeToSeekThrough.split(t);
node.addChild(i - 1, splitResult.getNewNode());
node.addKey(splitResult.getSplitKey());
if(splitResult.getSplitKey().compareTo(key) > 0) {
i++;
}
}
insertNonFull(node.getChildren().get(i - 1), key);
}
}
private int findRightChildIndexToSearchThrough(Node node, String key) {
int i = node.getNumberOfKeys();
while(i >= 1 && key.compareTo(node.getKeys().get(i - 1)) < 0) {
i--;
}
i++;
return i;
}
public BTreeSearchResult search(String key) {
return searchInNode(root, key);
}
private BTreeSearchResult searchInNode(Node node, String key) {
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);
}
if(node.isLeaf()) return new BTreeSearchResult();
return searchInNode(node.getChildren().get(i), key);
}
}

View File

@ -0,0 +1,30 @@
package be.brainbaking.datastructures.trees;
public class BTreeSearchResult {
private final Node node;
private final int index;
public BTreeSearchResult() {
node = null;
index = -1;
}
public boolean isFound() {
return node != null;
}
public BTreeSearchResult(Node node, int index) {
this.node = node;
this.index = index;
}
public int getIndex() {
return index;
}
public Node getNode() {
return node;
}
}

View File

@ -0,0 +1,122 @@
package be.brainbaking.datastructures.trees;
import java.util.ArrayList;
import java.util.List;
/**
* Each node has the following fields:
* n[x], the number of keys
* the n[x] keys themselves, stored in nondecreasing order key1[x] <= key2[x] <= ... <= keyn[x]
* leaf[x], a boolean value that is TRUE if x is a leaf and FALSE if x is an internal node
*
* each internal node x also contains n[x] + 1 pointers cn[x] to its children.
* The keys keyi[x] separate the ranges of keys stored in each subtree:
* if ki is any key stored in the subtree with root ci[x], then
* k1 <= key1[x] <= k2 <= key2[x]
*
* All leaves have the same depth, which is the tree's height h.
* There are lower and upper bounds on the number of keys a node can contain. (fixed int, "t" >= 2)
* min. t - 1 keys
* max. 2t - 1 keys: "full"
*/
public class Node {
private boolean leaf;
private List<String> keys;
private List<Node> children;
public boolean isFull(int t) {
return getNumberOfKeys() >= 2* t - 1;
}
public Node(boolean leaf) {
keys = new ArrayList<>();
children = new ArrayList<>();
this.leaf = leaf;
}
public void addKey(String key) {
int i;
for(i = 0; i < keys.size(); i++) {
if(keys.get(i).compareTo(key) >= 0) {
break;
}
}
keys.add(i, key);
}
public void addChild(Node node) {
addChild(children.size(), node);
}
public void addChild(int index, Node node) {
// dit lijkt mij nog een performance hit te zijn die niet in default in BTrees zit
// ik wou het object-oriented aanpakken maar moet hier dan nog max O(index) aflopen.
children.add(index, node);
}
public NodeSplitResult split(int t) {
if(!isFull(t)) throw new UnsupportedOperationException("node niet vol, split zelf maar wa jong");
Node newNode = new Node(isLeaf());
for(int i = 0; i < t - 1; i++) {
newNode.addKey(keys.get(i + t));
}
// ?? Aantal sleutels en aantal children kan niet gelijk zijn?
if(!isLeaf()) {
for(int i = 0; i < t; i++) {
newNode.children.add(children.get(i + t - 1));
}
}
List<String> newKeys = new ArrayList<>();
for(int i = 0; i < t - 1; i++) {
newKeys.add(keys.get(i));
}
String key = keys.get(t - 1);
keys = newKeys;
if(!isLeaf()) {
List<Node> newChildren = new ArrayList<>();
for(int i = 0; i < t; i++) {
newChildren.add(children.get(i));
}
children = newChildren;
}
return new NodeSplitResult(newNode, key);
}
public static Node createRoot() {
return new Node(true);
}
public int getNumberOfKeys() {
return keys.size();
}
public List<Node> getChildren() {
return children;
}
public List<String> getKeys() {
return keys;
}
public boolean isLeaf() {
return leaf;
}
public static Node createFromSplitResult(Node node, NodeSplitResult splitResult) {
Node root = new Node(false);
root.addKey(splitResult.getSplitKey());
root.addChild(node);
root.addChild(splitResult.getNewNode());
return root;
}
}

View File

@ -0,0 +1,20 @@
package be.brainbaking.datastructures.trees;
public class NodeSplitResult {
private final Node newNode;
private final String splitKey;
public Node getNewNode() {
return newNode;
}
public String getSplitKey() {
return splitKey;
}
public NodeSplitResult(Node newNode, String splitKey) {
this.newNode = newNode;
this.splitKey = splitKey;
}
}

View File

@ -0,0 +1,69 @@
package be.brainbaking.datastructures.trees;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BTreeTest {
@Test
public void searching_afterAddingExampleAndHavingToSplit() {
BTree tree = new BTree(4);
tree.add("A");
tree.add("D");
tree.add("F");
tree.add("H");
tree.add("L");
tree.add("N");
tree.add("P");
tree.add("Q");
assertEquals(1, tree.search("D").getIndex());
assertEquals(0, tree.search("H").getIndex()); // want die key is naar boven verhuisd bij splitten
assertEquals(2, tree.search("P").getIndex());
assertEquals(3, tree.search("Q").getIndex());
}
@Test
public void addingOnly() {
BTree tree = new BTree(4);
tree.add("A");
tree.add("D");
tree.add("F");
tree.add("H");
tree.add("L");
tree.add("N");
tree.add("P");
// nog eentje toevoegen zou moeten splitsen (4*2 groot)
tree.add("Q");
assertArrayEquals(Arrays.asList("H").toArray(), tree.getRoot().getKeys().toArray());
assertArrayEquals(Arrays.asList("A", "D", "F").toArray(), tree.getRoot().getChildren().get(0).getKeys().toArray());
assertArrayEquals(Arrays.asList("L", "N", "P", "Q").toArray(), tree.getRoot().getChildren().get(1).getKeys().toArray());
}
@Test
public void simpleOperations_addAndSearch() {
BTree tree = new BTree(5);
tree.add("key1");
BTreeSearchResult result = tree.search("key1");
assertEquals(true, result.isFound());
}
@Test
public void simpleOperations_searchKeyNotFound() {
BTree tree = new BTree(5);
tree.add("key100");
BTreeSearchResult result = tree.search("key1");
assertEquals(false, result.isFound());
}
}

View File

@ -0,0 +1,71 @@
package be.brainbaking.datastructures.trees;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
public class NodeTest {
@Test
public void createFromSplitResult_nodeALeftAndBRight() {
Node root = new Node(false);
root.addKey("R");
Node node = new Node(true);
node.addKey("A");
NodeSplitResult result = new NodeSplitResult(node, "B");
Node newRoot = Node.createFromSplitResult(root, result);
assertSame(root, newRoot.getChildren().get(0));
assertSame(node, newRoot.getChildren().get(1));
}
@Test
public void addKey_addsToCorrectPositionAutomatically() {
Node node = new Node(true);
node.addKey("A");
node.addKey("C");
node.addKey("B");
assertArrayEquals(Arrays.asList("A", "B", "C").toArray(), node.getKeys().toArray());
}
@Test
public void split() {
Node node = new Node(false);
Node child = new Node(true);
node.addChild(child);
node.addKey("N");
node.addKey("W");
child.addKey("P");
child.addChild(new Node(true));
child.addKey("Q");
child.addChild(new Node(true));
child.addKey("R");
child.addChild(new Node(true));
child.addKey("S");
child.addChild(new Node(true));
child.addKey("T");
child.addChild(new Node(true));
child.addKey("U");
child.addChild(new Node(true));
child.addKey("V");
child.addChild(new Node(true));
NodeSplitResult result = child.split(4);
assertEquals(3, result.getNewNode().getNumberOfKeys());
assertEquals(3, child.getNumberOfKeys());
assertEquals("S", result.getSplitKey());
assertArrayEquals(Arrays.asList("P", "Q", "R").toArray(), child.getKeys().toArray());
assertArrayEquals(Arrays.asList("T", "U", "V").toArray(), result.getNewNode().getKeys().toArray());
}
}