diff --git a/TreeLib.iml b/TreeLib.iml index c90834f..e4b5f69 100644 --- a/TreeLib.iml +++ b/TreeLib.iml @@ -4,8 +4,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/out/production/TreeLib/Main.class b/out/production/TreeLib/Main.class new file mode 100644 index 0000000..9b14f1b Binary files /dev/null and b/out/production/TreeLib/Main.class differ diff --git a/out/production/TreeLib/binarytree/BinaryTree.class b/out/production/TreeLib/binarytree/BinaryTree.class new file mode 100644 index 0000000..5a10ef2 Binary files /dev/null and b/out/production/TreeLib/binarytree/BinaryTree.class differ diff --git a/out/production/TreeLib/binarytree/BinaryTreeNode.class b/out/production/TreeLib/binarytree/BinaryTreeNode.class new file mode 100644 index 0000000..a48050d Binary files /dev/null and b/out/production/TreeLib/binarytree/BinaryTreeNode.class differ diff --git a/out/production/TreeLib/graph/Node.class b/out/production/TreeLib/graph/Node.class new file mode 100644 index 0000000..608ce7d Binary files /dev/null and b/out/production/TreeLib/graph/Node.class differ diff --git a/out/production/TreeLib/graph/Tree.class b/out/production/TreeLib/graph/Tree.class new file mode 100644 index 0000000..3b05f3a Binary files /dev/null and b/out/production/TreeLib/graph/Tree.class differ diff --git a/out/test/TreeLib/BinaryTreeTests.class b/out/test/TreeLib/BinaryTreeTests.class new file mode 100644 index 0000000..1fbd2ce Binary files /dev/null and b/out/test/TreeLib/BinaryTreeTests.class differ diff --git a/src/binarytree/BinaryTree.java b/src/binarytree/BinaryTree.java new file mode 100644 index 0000000..41d0b60 --- /dev/null +++ b/src/binarytree/BinaryTree.java @@ -0,0 +1,168 @@ +package binarytree; + +import graph.Node; +import graph.Tree; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +/** + * A binary tree is a tree where every node has AT MOST 2 child nodes + */ +public class BinaryTree> extends Tree { + + public BinaryTree() { + this(null); + } + + public BinaryTree(T initialValue) { + this.root = new BinaryTreeNode<>(null, initialValue); // initialize the root + } + + /** + * Adds a new node to the tree + * + * @param value value of the node to be added + * @return true if the addition was successful, false otherwise + */ + @Override + public Node addNode(T value) { + this.values.add(value); + + // if root has not been initialized yet + if (this.root.getValue() == null) { + this.nodes.add(root); + + this.root.setValue(value); + + return root; + } + + // root has been initialized, so call on the root to add the value + Node newNode = root.addNode(value); + + this.nodes.add(newNode); + + return newNode; + } + + /** + * removes a node from the tree, based on value + * + * @param value the value of the node to be removed + * @return true if the node was successfully removed, false otherwise + */ + @Override + public boolean removeNode(T value) { + if (!this.values.contains(value)) { + return false; + } + + Node node = findNode(value); + + List> children = new ArrayList<>(node.getChildren()); + + // remove the node + this.values.remove(value); + this.nodes.remove(node); + + if(node.getParent() != null) { + node.getParent().getChildren().remove(node); + this.nodes.remove(node); + } else { // if the value had no parent, it was the root node + // set the root node to be the first node of the children list + this.root = children.get(0); + } + + // binary tree does not put any restrictions on what order the children are in the tree + // re-introduce all children into the root + + // it could happen that we have removed the root node and set the first child to be the new root + // if so, only reintroduce the other children, omitting the first one + int start = 0; + if(node.getParent() == null) { + start = 1; + } + + for (int i = start; i < children.size(); i++) { + Node child = children.get(i); + this.root.addNode(child.getValue()); + } + + return true; + } + + /** + * Removes the passed node + * + * @param node node to be removed + * @return true if the removal was successful, false otherwise + */ + @Override + public boolean removeNode(Node node) { + if (!this.nodes.contains(node)) { + return false; + } + + // remove the node + this.values.remove(node.getValue()); + this.nodes.remove(node); + node.getParent().getChildren().remove(node); + + // binary tree does not put any restrictions on what order the children are in the tree + // re-introduce all children into the parent + for (Node child : node.getChildren()) { + node.getParent().addNode(child.getValue()); + } + + return true; + } + + /** + * Finds the node with passed value + * + * @param value the value the required node should have + * @return the required node if one is present, null otherwise + */ + @Override + public Node findNode(T value) { + if (!this.values.contains(value)) { + return null; + } + + // because Binary Tree does not put ANY restrictions on what order the children are in, + // we need search through the entire tree + // we shall perform a BFS to do this + Queue> queue = new ArrayDeque<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + Node current = queue.poll(); + if (current.getValue().equals(value)) { + return current; + } + + for (Node child : current.getChildren()) { + queue.offer(child); + } + } + + return null; + } + + @Override + public String toString() { + if(nodes.size() == 0) { + return "Empty"; + } + + StringBuilder sb = new StringBuilder(); + for(Node n : nodes) { + sb.append(n.toString()); + } + + return sb.toString(); + } +} diff --git a/src/binarytree/BinaryTreeNode.java b/src/binarytree/BinaryTreeNode.java new file mode 100644 index 0000000..a1eb26e --- /dev/null +++ b/src/binarytree/BinaryTreeNode.java @@ -0,0 +1,78 @@ +package binarytree; + +import graph.Node; + +import java.util.Random; + +public class BinaryTreeNode> extends Node { + public BinaryTreeNode(Node parent, T item) { + super(parent, item); + } + + /** + * Adds a new node to the current node + * if the numbers of nodes is larger than 2, proceed to the next node + * + * @param value value to be added + * @return the new node if adding was successful, null otherwise + */ + @Override + public Node addNode(T value) { + if (children.size() < 2) { + Node newChild = new BinaryTreeNode(this, value); + this.addNode(newChild); + + return newChild; + } + + // pick a random child out of the current children + Node randomChild = this.children.get(new Random().nextInt(this.children.size())); + + return randomChild.addNode(value); + } + + private void addNode(Node node) { + if (children.size() < 2) { // we can still add a node + this.children.add(node); + } else { + + // pick a random child out of the current children + Node randomChild = this.children.get(new Random().nextInt(this.children.size())); + + randomChild.addNode(node.getValue()); + } + } + + @Override + public int hashCode() { + int randomPrime = 13; // chosen randomly + + int sum = this.children.stream() + .map(Node::hashCode) + .reduce(0, Integer::sum); + + return (this.value.hashCode() + sum) * randomPrime; + } + + @Override + public boolean equals(Object that) { + if (that instanceof Node) { + return ((Node) that).getValue().equals(value); + } + + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("BT(%s)", value)); + + for(Node child : children) { + sb.append(":"); + sb.append(child.toString()); + } + + return sb.toString(); + } +} diff --git a/src/graph/Graph.java b/src/graph/Graph.java deleted file mode 100644 index 3948543..0000000 --- a/src/graph/Graph.java +++ /dev/null @@ -1,11 +0,0 @@ -package graph; - -import java.util.List; - -public abstract class Graph { - protected List nodes; - - public List getAllNodes() { - return nodes; - } -} diff --git a/src/graph/Node.java b/src/graph/Node.java index 5f4d687..c6bb444 100644 --- a/src/graph/Node.java +++ b/src/graph/Node.java @@ -1,20 +1,46 @@ package graph; +import java.util.ArrayList; import java.util.List; -public abstract class Node { - private final String item; - private List children; +public abstract class Node> { + protected T value; + protected Node parent; + protected final List> children; - public Node(String item) { - this.item = item; + public Node(Node parent, T value) { + this.value = value; + this.parent = parent; + this.children = new ArrayList<>(); } - public String getItem() { - return item; + public T getValue() { + return value; } - public List getChildren() { + public void setValue(T value) { + this.value = value; + } + + public List> getChildren() { return children; } + + public Node getParent() { + return parent; + } + + public abstract Node addNode(T value); + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + public abstract String toString(); } diff --git a/src/graph/Tree.java b/src/graph/Tree.java new file mode 100644 index 0000000..9630a08 --- /dev/null +++ b/src/graph/Tree.java @@ -0,0 +1,35 @@ +package graph; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class Tree> { + protected Node root; + protected List> nodes; + protected Set values; + + public Tree() { + this.nodes = new ArrayList<>(); + this.values = new HashSet<>(); + } + + public List> getAllNodes() { + return nodes; + } + + public int getSize() { + return nodes.size(); + } + + public Node getRoot() { + return root; + } + + public abstract Node addNode(T value); + public abstract boolean removeNode(T value); + public abstract boolean removeNode (Node n); + public abstract Node findNode(T value); + public abstract String toString(); +} diff --git a/tests/BinaryTreeTests.java b/tests/BinaryTreeTests.java new file mode 100644 index 0000000..b36be50 --- /dev/null +++ b/tests/BinaryTreeTests.java @@ -0,0 +1,55 @@ +import binarytree.BinaryTree; +import binarytree.BinaryTreeNode; +import graph.Node; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class BinaryTreeTests { + BinaryTree tree; + + @Before + public void init() { + this.tree = new BinaryTree<>(); + } + + @Test + public void testAddition() { + Node n1 = tree.addNode(1); + Node n2 = tree.addNode(2); + + Assert.assertEquals(2, tree.getSize()); + Assert.assertEquals(n1, tree.findNode(1)); + Assert.assertEquals(n2, tree.findNode(2)); + Assert.assertNull(tree.findNode(3)); + Assert.assertEquals(1, tree.getRoot().getValue().intValue()); + Assert.assertEquals(2, tree.getRoot().getChildren().get(0).getValue().intValue()); + } + + @Test + public void testAddition2() { + tree.addNode(1); + tree.addNode(2); + tree.addNode(3); + tree.addNode(4); + + Assert.assertEquals(4, tree.getSize()); + } + + @Test + public void testAddition3() { + Node n1 = new BinaryTreeNode<>(null, 1); + } + + @Test + public void testRemoval() { + tree.addNode(1); + tree.addNode(2); + System.out.println(tree); + + tree.removeNode(1); + System.out.println(tree); + Assert.assertEquals(1, tree.getSize()); + Assert.assertEquals(2, tree.getRoot().getValue().intValue()); + } +}