diff --git a/src/aud/exam/prep/tree/BinaryTreeIterator.java b/src/aud/exam/prep/tree/BinaryTreeIterator.java new file mode 100644 index 0000000..1cdc816 --- /dev/null +++ b/src/aud/exam/prep/tree/BinaryTreeIterator.java @@ -0,0 +1,57 @@ +package aud.exam.prep.tree; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +class BinaryTreeIterator implements Iterator { + + private final Stack stack = new Stack<>(); + + BinaryTreeIterator(BinaryTreeNode node) { + if (node != null) { + var frame = new StackFrame(); + frame.node = node; + stack.push(frame); + } + } + + @Override + public boolean hasNext() { + return !stack.empty(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + var frame = stack.peek(); + + while (!frame.pushedLeft) { + frame.pushedLeft = true; + + if (frame.node.left != null) { + var newFrame = new StackFrame(); + newFrame.node = frame.node.left; + stack.push(newFrame); + frame = newFrame; + } + } + + stack.pop(); + + if (frame.node.right != null) { + var newFrame = new StackFrame(); + newFrame.node = frame.node.right; + stack.push(newFrame); + } + + return frame.node.key; + } + + private class StackFrame { + boolean pushedLeft = false; + BinaryTreeNode node; + } +} diff --git a/src/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessor.java b/src/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessor.java new file mode 100644 index 0000000..b73ccc2 --- /dev/null +++ b/src/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessor.java @@ -0,0 +1,36 @@ +package aud.exam.prep.tree; + +abstract class OrderedBinaryTreeNodeProcessor implements OrderedTreeProcessor> { + @Override + public BinaryTreeNode newEmptyTree() { + return null; + } + + @Override + public Iterable iterate(BinaryTreeNode tree) { + return () -> + new BinaryTreeIterator<>(tree); + } + + @Override + public void print(BinaryTreeNode tree) { + printRec(tree, 0); + } + + private void printRec(BinaryTreeNode tree, int indent) { + if (tree == null) { + System.out.println("- *"); + return; + } + + System.out.println("-> " + tree.key); + + System.out.print(" |".repeat(indent)); + System.out.print(" L"); + printRec(tree.left, indent + 1); + + System.out.print(" |".repeat(indent)); + System.out.print(" L"); + printRec(tree.right, indent + 1); + } +} \ No newline at end of file diff --git a/src/aud/exam/prep/tree/OrderedTreeProcessor.java b/src/aud/exam/prep/tree/OrderedTreeProcessor.java new file mode 100644 index 0000000..4424124 --- /dev/null +++ b/src/aud/exam/prep/tree/OrderedTreeProcessor.java @@ -0,0 +1,82 @@ +package aud.exam.prep.tree; + +import java.util.Comparator; + +/** + * Instances of this interface can work with any "valid" tree of type T. + * For what is valid, see {@link #check(T, Comparator)}. + * For this processor trees always have to be "in order". + * This is a literal translation of the "inspiration sheet" into java. + * + * @param Type of elements stored in T + * @param Type of the tree + */ +public interface OrderedTreeProcessor { + + boolean find(T t, V v, Comparator cmp); + + boolean override(T t, V from, V to, Comparator cmp); + + boolean overrideAll(T t, V from, V to, Comparator cmp); + + T insert(T t, V v, Comparator cmp); + + boolean remove(T t, V v, Comparator cmp); + + boolean removeAll(T t, V v, Comparator cmp); + + V max(T t, Comparator cmp); + + V secondMax(T t, Comparator cmp); + + int height(T t); + + boolean isBalanced(T t); + + int numberOfNodes(T t); + + int numberOfNodesOnLevel(T t, int level); + + T rightmostNodeInLeftSubtree(T t); + + T leftmostNodeInRightSubtree(T t); + + T invert(T t); + + T clone(T t); + + /** + * Check whether ot not the given tree is "valid" according to + * what is considered valid depends on the specific data structure. + * This is also used by the tests to verify implementations. + * This has to be tested separate for each data structure. + * + * @param t A tree + * @return true iff the given tree is valid + */ + boolean check(T t, Comparator cmp); + + /** + * Create an empty tree. + * + * @return A new, empty tree + */ + T newEmptyTree(); + + /** + * Return an {@link Iterable}, or a lambda returning {@link java.util.Iterator}, + * over the elements of the tree t. + * This is required by the tests. + * + * @param t A tree + * @return An {@link Iterable} over t + */ + Iterable iterate(T t); + + /** + * Print a tree to standard out + * + * @param t A tree + */ + void print(T t); +} \ No newline at end of file diff --git a/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java b/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java new file mode 100644 index 0000000..4fe012f --- /dev/null +++ b/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java @@ -0,0 +1,131 @@ +package aud.exam.prep.tree; + +import java.util.Comparator; + +public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTreeNodeProcessor { + + @Override + public boolean find(BinaryTreeNode tree, V v, Comparator cmp) { + return false; + } + + @Override + public boolean override(BinaryTreeNode pointer, V from, V to, Comparator cmp) { + return false; + } + + @Override + public boolean overrideAll(BinaryTreeNode pointer, V from, V to, Comparator cmp) { + return false; + } + + @Override + public BinaryTreeNode insert(BinaryTreeNode tree, V v, Comparator cmp) { + if (tree == null) { + tree = new BinaryTreeNode<>(); + tree.key = v; + return tree; + } + + var c = cmp.compare(v, tree.key); + + if (c <= 0) { + tree.left = insert(tree.left, v, cmp); + } else { + tree.right = insert(tree.right, v, cmp); + } + + return tree; + } + + @Override + public boolean remove(BinaryTreeNode tree, V v, Comparator cmp) { + return false; + } + + @Override + public boolean removeAll(BinaryTreeNode tree, V v, Comparator cmp) { + return false; + } + + @Override + public V max(BinaryTreeNode tree, Comparator cmp) { + return null; + } + + @Override + public V secondMax(BinaryTreeNode tree, Comparator cmp) { + return null; + } + + @Override + public int height(BinaryTreeNode tree) { + if (tree == null) { + return -1; + } + return 0; + } + + @Override + public boolean isBalanced(BinaryTreeNode tree) { + if (tree == null) { + return true; + } + return false; + } + + @Override + public int numberOfNodes(BinaryTreeNode tree) { + return 0; + } + + @Override + public int numberOfNodesOnLevel(BinaryTreeNode tree, int level) { + return 0; + } + + @Override + public BinaryTreeNode rightmostNodeInLeftSubtree(BinaryTreeNode tree) { + return null; + } + + @Override + public BinaryTreeNode leftmostNodeInRightSubtree(BinaryTreeNode tree) { + return null; + } + + @Override + public BinaryTreeNode invert(BinaryTreeNode tree) { + return null; + } + + @Override + public BinaryTreeNode clone(BinaryTreeNode tree) { + return null; + } + + @Override + public boolean check(BinaryTreeNode tree, Comparator cmp) { + if (tree == null) { + return true; + } + + if (tree.key == null) { + return false; + } + + if (tree.left != null) { + if (!check(tree.left, cmp) || cmp.compare(tree.left.key, tree.key) > 0) { + return false; + } + } + + if (tree.right != null) { + if (!check(tree.right, cmp) || cmp.compare(tree.right.key, tree.key) < 0) { + return false; + } + } + + return true; + } +} diff --git a/src/aud/exam/prep/tree/Stack.java b/src/aud/exam/prep/tree/Stack.java new file mode 100644 index 0000000..81c1408 --- /dev/null +++ b/src/aud/exam/prep/tree/Stack.java @@ -0,0 +1,36 @@ +package aud.exam.prep.tree; + +import aud.exam.prep.array.Arrays; + +@SuppressWarnings("ManualArrayCopy") +public class Stack { + + private int size = 0; + private T[] theStack = Arrays.newArray(10); + + public void push(T t) { + while (size >= theStack.length) { + T[] newStack = Arrays.newArray(theStack.length * 2); + + for (int i = 0; i < theStack.length; i++) { + newStack[i] = theStack[i]; + } + + theStack = newStack; + } + + theStack[size++] = t; + } + + public T peek() { + return theStack[size-1]; + } + + public T pop() { + return theStack[--size]; + } + + public boolean empty() { + return size == 0; + } +} diff --git a/test/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessorTest.java b/test/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessorTest.java new file mode 100644 index 0000000..360c3ae --- /dev/null +++ b/test/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessorTest.java @@ -0,0 +1,96 @@ +package aud.exam.prep.tree; + +import org.junit.jupiter.api.Test; + +import static aud.exam.prep.Tests.CMP; +import static org.junit.jupiter.api.Assertions.*; + +public abstract class OrderedBinaryTreeNodeProcessorTest extends OrderedTreeProcessorTest> { + + protected OrderedBinaryTreeNodeProcessorTest(OrderedTreeProcessor> processor) { + super(processor); + } + + @Test + void testThat_checkOfNull() { + assertTrue(processor.check(null, CMP)); + } + + @Test + void testThat_checkOfNoChildrenIsTrue() { + var t = new BinaryTreeNode(); + t.key = 2; + assertTrue(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfNoKeyIsFalse() { + var t = new BinaryTreeNode(); + assertFalse(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfLeftGreaterIsFalse() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.left = new BinaryTreeNode<>(); + t.left.key = 10; + + assertFalse(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfLeftSmallerIsTrue() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.left = new BinaryTreeNode<>(); + t.left.key = 2; + + assertTrue(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfRightGreaterIsTrue() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.right = new BinaryTreeNode<>(); + t.right.key = 10; + + assertTrue(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfRightSmallerIsFalse() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.right = new BinaryTreeNode<>(); + t.right.key = 2; + + assertFalse(processor.check(t, CMP)); + } + + @Test + void testThat_checkOfBadRightIsFalse() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.right = new BinaryTreeNode<>(); + + assertFalse(processor.check(t, CMP)); + } + + + @Test + void testThat_checkOfLeftIsFalse() { + var t = new BinaryTreeNode(); + t.key = 5; + + t.left = new BinaryTreeNode<>(); + + assertFalse(processor.check(t, CMP)); + } +} \ No newline at end of file diff --git a/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java b/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java new file mode 100644 index 0000000..5504803 --- /dev/null +++ b/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java @@ -0,0 +1,55 @@ +package aud.exam.prep.tree; + +import aud.exam.prep.ListProvider; +import aud.exam.prep.tree.OrderedTreeProcessor; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.util.List; + +import static aud.exam.prep.Tests.CMP; +import static org.junit.jupiter.api.Assertions.*; + +public abstract class OrderedTreeProcessorTest { + + protected final OrderedTreeProcessor processor; + + protected OrderedTreeProcessorTest(OrderedTreeProcessor processor) { + this.processor = processor; + } + + @Test + void testThat_newEmptyTreeWorks() { + T t = processor.newEmptyTree(); + + assertTrue(processor.check(t, CMP)); + + assertEquals(-1, processor.height(t)); + + assertTrue(processor.isBalanced(t)); + + assertEquals(0, processor.numberOfNodes(t)); + + for (int i = 0; i < 10; i++) { + assertEquals(0, processor.numberOfNodesOnLevel(t, i)); + } + + assertIterableEquals(List.of(), processor.iterate(t)); + } + + + @ParameterizedTest + @ArgumentsSource(ListProvider.class) + void testThat_insertAndIterateWork(List list) { + T t = processor.newEmptyTree(); + + for (var n : list) { + t = processor.insert(t, n, CMP); + } + list.sort(CMP); + + assertTrue(processor.check(t, CMP)); + assertIterableEquals(list, processor.iterate(t)); + } +} \ No newline at end of file diff --git a/test/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessorTest.java b/test/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessorTest.java new file mode 100644 index 0000000..4584861 --- /dev/null +++ b/test/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessorTest.java @@ -0,0 +1,8 @@ +package aud.exam.prep.tree; + +public class RecursiveOrderedBinaryTreeNodeProcessorTest extends OrderedBinaryTreeNodeProcessorTest { + + public RecursiveOrderedBinaryTreeNodeProcessorTest() { + super(new RecursiveOrderedBinaryTreeNodeProcessor<>()); + } +} \ No newline at end of file