Start implementing BTree class

This commit is contained in:
rec0de 2018-05-23 15:15:24 +02:00
parent b0784f990f
commit 24a2536d65
5 changed files with 345 additions and 97 deletions

7
BTree/.classpath Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
BTree/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bin/

17
BTree/.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>BTree</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -1,6 +1,11 @@
package lab;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.stream.Collectors;
import frame.*;
@ -17,75 +22,97 @@ import frame.*;
public class B_Tree {
private final int t;
private B_TreeNode root;
/**
* The constructor
*
* @param t minimum degree of the B-tree.
* t can not be changed once a new B Tree object is created.
*
* t can not be changed once a new B Tree object is created.
*
*/
public B_Tree(int t) {
/**
* Add your code here
*/
this.t = t;
this.root = new B_TreeNode(t);
}
/**
* This method takes as input the name of a file containing a sequence of
* entries that should be inserted to the B-Tree in the order they appear in
* the file. You cannot make any assumptions on the order of the entries nor
* is it allowed to change the order given in the file. You can assume that the
* file is located in the same directory as the executable program. The input
* file is similar to the input file for lab 1. The return value is the number of
* entries successfully inserted in the B-Tree.
* entries that should be inserted to the B-Tree in the order they appear in
* the file. You cannot make any assumptions on the order of the entries nor
* is it allowed to change the order given in the file. You can assume that the
* file is located in the same directory as the executable program. The input
* file is similar to the input file for lab 1. The return value is the number of
* entries successfully inserted in the B-Tree.
*
* @param filename name of the file containing the entries
* @return returns the number of entries successfully inserted in the B-Tree.
*/
public int constructB_TreeFromFile (String filename) {
/**
* Add your code here
*/
return 0;
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
String[] parts;
int insertionCount = 0;
// Read all lines
while ((line = br.readLine()) != null) {
// Create new entry from line parts separated by a semicolon
parts = line.split(";");
Entry entry = new Entry(parts[0], parts[1], parts[2]);
if(this.insert(entry))
insertionCount += 1;
}
return insertionCount;
} catch (FileNotFoundException e) {
System.out.println("File not found");
return 0;
} catch (IOException e) {
System.out.println("IO Exception");
return 0;
}
}
/**
* This method inserts the entry insertEntry into the B-Tree. Note that you
* have to deal with overflows if you want to insert an entry into a leaf which
* already contains 2t - 1 entries. This method returns true if the insertion
* of the entry insertEntry is successful and false if the key of this entry
* already exists in the B-Tree.
*
* have to deal with overflows if you want to insert an entry into a leaf which
* already contains 2t - 1 entries. This method returns true if the insertion
* of the entry insertEntry is successful and false if the key of this entry
* already exists in the B-Tree.
*
* @param insertEntry entry to insert into the B-Tree
* @return returns true if the entry insertEntry is successfully inserted
* false if the entry already exists in the B-Tree
* false if the entry already exists in the B-Tree
*/
public boolean insert(Entry insertEntry) {
/**
* Add your code here
*/
return true;
if(root.isFull()) {
B_TreeNode newRoot = new B_TreeNode(this.t);
newRoot.makeNonLeaf();
newRoot.setPointer(0, root);
newRoot.splitChild(root);
this.root = newRoot;
}
return root.insert(insertEntry);
}
/**
* This method deletes the entry from the B-Tree structure, having deleteKey
* as key. In this method you have to distinguish between two cases:
* 1. The entry, having deleteKey as key, is located in an internal node.
* 2. The entry, having deleteKey as key, is located in a leaf.
* This method returns the entry, having deleteKey as key if the deletion is
* successful and null if the key deleteKey is not found in any entry of the
* B-Tree.
*
* as key. In this method you have to distinguish between two cases:
* 1. The entry, having deleteKey as key, is located in an internal node.
* 2. The entry, having deleteKey as key, is located in a leaf.
* This method returns the entry, having deleteKey as key if the deletion is
* successful and null if the key deleteKey is not found in any entry of the
* B-Tree.
*
* @param deleteKey key of the entry to delete from the B-Tree
* @return returns the deleted entry if the deletion ends successfully
* null if the entry is not found in the B-Tree
* null if the entry is not found in the B-Tree
*/
@ -98,83 +125,65 @@ public class B_Tree {
/**
* This method searches in the B-Tree for the entry with key searchKey. It
* returns the entry, having searchKey as key if such an entry is found, null
* otherwise.
*
* returns the entry, having searchKey as key if such an entry is found, null
* otherwise.
*
* @param searchKey key of the entry to find in the B-Tree
* @return returns the entry having searchKey as key if such an entry exists
* null if the entry is not found in the B-Tree
* null if the entry is not found in the B-Tree
*/
public Entry find(String searchKey) {
/**
* Add your code here
*/
return new Entry();
return root.find(searchKey);
}
/**
* This method returns a ArrayList<String> containing the output B-Tree.
* The output should be directly interpretable dot code.
* Each item in the ArrayList corresponds to one line of the output
* tree. The nodes of the output tree should only
* contain the keys of the entries and not the data.
*
* The output should be directly interpretable dot code.
* Each item in the ArrayList corresponds to one line of the output
* tree. The nodes of the output tree should only
* contain the keys of the entries and not the data.
*
* @return returns the output B-Tree in directly interpretable dot code
*/
*/
public ArrayList<String> getB_Tree() {
/**
* Add your code here
*/
return new ArrayList<>();
ArrayList<String> res = new ArrayList<String>();
res.add("digraph{");
res.add("node[shape=record];");
root.getDotCode(1, true, res);
res.add("}");
return res;
}
/**
* This method returns the height of the B-Tree
* If the B-Tree is empty this method should return 0.
*
* If the B-Tree is empty this method should return 0.
*
* @return returns the height of the B-Tree
*/
public int getB_TreeHeight() {
/**
* Add your code here
*/
return 0;
return root.getHeight();
}
/**
* This method traverses the B-Tree in inorder and adds each entry to a
* ArrayList<Entry>. The returned ArrayList contains the entries of the B-Tree
* in ascending order.
*
* ArrayList<Entry>. The returned ArrayList contains the entries of the B-Tree
* in ascending order.
*
* @return returns the entries stored in the B-Tree in ascending order
*/
public ArrayList<Entry> getInorderTraversal() {
/**
* Add your code here
*/
return new ArrayList<>();
ArrayList<Entry> res = root.inorderTraversal();
System.out.println(res.stream().map(Object::toString).collect(Collectors.joining(", ")));
return res;
}
/**
* This method returns the number of entries in the B-Tree (not the number
* of nodes).
*
*
* of nodes).
*
* @return returns the size of the B-Tree, i.e., the number of entries stored in the B-Tree
*/
public int getB_TreeSize() {
/**
* Add your code here
*/
return 0;
return root.getSize();
}
}

View File

@ -1,5 +1,9 @@
package lab;
import java.util.ArrayList;
import frame.Entry;
/*
* Implements a node of a B-Tree
*
@ -9,25 +13,235 @@ package lab;
*/
public class B_TreeNode {
private ArrayList<Entry> keys;
private B_TreeNode[] pointers;
private int currentLoad = 0;
private boolean isLeaf = true;
private final int t;
/**
* The constructor
*
* @param t minimum degree of the B-tree
*
*/
public B_TreeNode(int t) {
/**
* Add your code here
*/
keys = new ArrayList<Entry>(t*2-1);
pointers = new B_TreeNode[t*2];
this.t = t;
}
public Entry find(String searchKey) {
Entry dummy = new Entry(searchKey, "", "");
System.out.println("Searching for "+searchKey+" in "+this);
int index = getIndexOf(dummy);
if(index > -1) {
System.out.println("Is here!");
return keys.get(index);
}
else if(isLeaf) {
System.out.println("Is not in leaf -> is not in tree");
return null;
}
else {
System.out.println("Pointing to "+getPointerTowards(dummy));
return getPointerTowards(dummy).find(searchKey);
}
}
public int getIndexOf(Entry entry) {
for(int i = 0; i < this.currentLoad; i++) {
if(entry.compareTo(keys.get(i)) == 0)
return i;
}
return -1;
}
public boolean insert(Entry entry) {
if(isLeaf)
return getIndexOf(entry) > 0 ? false : insertValue(entry);
if(this.isFull())
throw new RuntimeException("Insert expects non-full root node");
B_TreeNode pointer = getPointerTowards(entry);
System.out.println(pointer);
if(pointer.isFull()) {
this.splitChild(pointer);
pointer = getPointerTowards(entry);
}
return pointer.insert(entry);
}
private boolean insertValue(Entry entry) {
if(!this.isLeaf)
throw new RuntimeException("Trying to insert value into non-leaf node");
if(this.isFull())
throw new RuntimeException("Trying to insert value into full leaf node");
int i = 0;
while(i < this.currentLoad && entry.compareTo(keys.get(i)) > 0) {
i += 1;
}
keys.add(i, entry);
this.currentLoad += 1;
return true;
}
private B_TreeNode getPointerTowards(Entry entry) {
if(this.isLeaf)
throw new RuntimeException("Trying to get pointer from leaf node");
int i = 0;
while(i < this.currentLoad && entry.compareTo(keys.get(i)) > 0) {
i += 1;
}
if(pointers[i] == null)
throw new RuntimeException("Non-leaf node with capacity "+currentLoad+" does not have pointer "+i);
return pointers[i];
}
public void splitChild(B_TreeNode child) {
if(this.isFull())
throw new RuntimeException("Trying to split child of full node");
System.out.println("SPLIT "+child);
B_TreeNode newRight = new B_TreeNode(this.t);
for(int i = 0; i < t-1; i++) {
newRight.insert(child.getEntry(i+t));
}
if(!child.isLeaf()) {
newRight.makeNonLeaf();
for(int i = 0; i < t; i++) {
newRight.setPointer(i, child.getPointer(i+t));
}
}
Entry median = child.getEntry(t-1);
child.setCurrentLoad(t-1);
int i = this.currentLoad;
while(pointers[i] != child) {
pointers[i+1] = pointers[i];
i -= 1;
}
pointers[i+1] = newRight;
keys.add(i, median);
currentLoad += 1;
System.out.println("New left node: "+child);
System.out.println("Median: "+median);
System.out.println("New right node: "+newRight);
}
public ArrayList<Entry> inorderTraversal(){
ArrayList<Entry> result = new ArrayList<Entry>();
if(pointers[0] != null) {
result.addAll(pointers[0].inorderTraversal());
}
for(int i = 0; i < this.currentLoad; i++) {
result.add(keys.get(i));
if(pointers[i+1] != null) {
result.addAll(pointers[i+1].inorderTraversal());
}
}
return result;
}
public int getHeight() {
if(isLeaf)
return 0;
int maxHeight = 0;
int cHeight;
for(int i = 0; i <= this.currentLoad; i++) {
if(pointers[i] != null) {
cHeight = pointers[i].getHeight();
if(cHeight > maxHeight)
maxHeight = cHeight;
}
}
return maxHeight + 1;
}
public int getSize() {
int res = this.currentLoad;
for(int i = 0; i <= this.currentLoad; i++) {
if(pointers[i] != null)
res += pointers[i].getSize();
}
return res;
}
public boolean isFull() {
return this.currentLoad >= this.t * 2 - 1;
}
public boolean isLeaf() {
return this.isLeaf;
}
public void makeNonLeaf() {
this.isLeaf = false;
}
public Entry getEntry(int i) {
return keys.get(i);
}
public B_TreeNode getPointer(int i) {
if(i > this.currentLoad)
throw new RuntimeException("Trying to get non-existent pointer");
return this.pointers[i];
}
public void setPointer(int i, B_TreeNode pointer) {
if(i > this.currentLoad)
throw new RuntimeException("Trying to set non-existent pointer");
if(isLeaf)
throw new RuntimeException("Trying to set pointer on leaf");
this.pointers[i] = pointer;
}
public void setCurrentLoad(int i) {
this.currentLoad = i;
}
public String toString() {
String res = "";
for(int i = 0; i < this.currentLoad; i++) {
res += keys.get(i).getKey() + ", ";
}
return "["+res+"]";
}
public int getDotCode(int index, boolean isRoot, ArrayList<String> list){
String name = (isRoot ? "root" : "node"+index);
String selfNode = name+"[label=\"<f0>*";
for(int i = 0; i < this.currentLoad*2; i += 2) {
selfNode += "|<f"+i+">"+keys.get(i/2).getKey()+"|<f"+(i+1)+">*";
}
selfNode += "\"];";
list.add(selfNode);
if(isLeaf)
return index;
for(int i = 0; i <= this.currentLoad; i++) {
list.add(name+":f"+(i*2)+"->node"+(index+1));
index = pointers[i].getDotCode(index+1, false, list);
}
return index;
}
/**
* Add your code here
*/
}