Partly implement RecursiveOrderedBinaryTreeNodeProcessor
This commit is contained in:
57
src/aud/exam/prep/tree/BinaryTreeIterator.java
Normal file
57
src/aud/exam/prep/tree/BinaryTreeIterator.java
Normal file
@ -0,0 +1,57 @@
|
||||
package aud.exam.prep.tree;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
class BinaryTreeIterator<T> implements Iterator<T> {
|
||||
|
||||
private final Stack<StackFrame> stack = new Stack<>();
|
||||
|
||||
BinaryTreeIterator(BinaryTreeNode<T> 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<T> node;
|
||||
}
|
||||
}
|
36
src/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessor.java
Normal file
36
src/aud/exam/prep/tree/OrderedBinaryTreeNodeProcessor.java
Normal file
@ -0,0 +1,36 @@
|
||||
package aud.exam.prep.tree;
|
||||
|
||||
abstract class OrderedBinaryTreeNodeProcessor<V> implements OrderedTreeProcessor<V, BinaryTreeNode<V>> {
|
||||
@Override
|
||||
public BinaryTreeNode<V> newEmptyTree() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<V> iterate(BinaryTreeNode<V> tree) {
|
||||
return () ->
|
||||
new BinaryTreeIterator<>(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(BinaryTreeNode<V> tree) {
|
||||
printRec(tree, 0);
|
||||
}
|
||||
|
||||
private void printRec(BinaryTreeNode<V> 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);
|
||||
}
|
||||
}
|
82
src/aud/exam/prep/tree/OrderedTreeProcessor.java
Normal file
82
src/aud/exam/prep/tree/OrderedTreeProcessor.java
Normal file
@ -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 <code>T</code>.
|
||||
* 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 <V> Type of elements stored in <code>T</code>
|
||||
* @param <T> Type of the tree
|
||||
*/
|
||||
public interface OrderedTreeProcessor<V, T> {
|
||||
|
||||
boolean find(T t, V v, Comparator<V> cmp);
|
||||
|
||||
boolean override(T t, V from, V to, Comparator<V> cmp);
|
||||
|
||||
boolean overrideAll(T t, V from, V to, Comparator<V> cmp);
|
||||
|
||||
T insert(T t, V v, Comparator<V> cmp);
|
||||
|
||||
boolean remove(T t, V v, Comparator<V> cmp);
|
||||
|
||||
boolean removeAll(T t, V v, Comparator<V> cmp);
|
||||
|
||||
V max(T t, Comparator<V> cmp);
|
||||
|
||||
V secondMax(T t, Comparator<V> 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<V> 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 <code>t</code>.
|
||||
* This is required by the tests.
|
||||
*
|
||||
* @param t A tree
|
||||
* @return An {@link Iterable} over t
|
||||
*/
|
||||
Iterable<V> iterate(T t);
|
||||
|
||||
/**
|
||||
* Print a tree to standard out
|
||||
*
|
||||
* @param t A tree
|
||||
*/
|
||||
void print(T t);
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package aud.exam.prep.tree;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class RecursiveOrderedBinaryTreeNodeProcessor<V> extends OrderedBinaryTreeNodeProcessor<V> {
|
||||
|
||||
@Override
|
||||
public boolean find(BinaryTreeNode<V> tree, V v, Comparator<V> cmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean override(BinaryTreeNode<V> pointer, V from, V to, Comparator<V> cmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overrideAll(BinaryTreeNode<V> pointer, V from, V to, Comparator<V> cmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTreeNode<V> insert(BinaryTreeNode<V> tree, V v, Comparator<V> 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<V> tree, V v, Comparator<V> cmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(BinaryTreeNode<V> tree, V v, Comparator<V> cmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V max(BinaryTreeNode<V> tree, Comparator<V> cmp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V secondMax(BinaryTreeNode<V> tree, Comparator<V> cmp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int height(BinaryTreeNode<V> tree) {
|
||||
if (tree == null) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBalanced(BinaryTreeNode<V> tree) {
|
||||
if (tree == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfNodes(BinaryTreeNode<V> tree) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfNodesOnLevel(BinaryTreeNode<V> tree, int level) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTreeNode<V> rightmostNodeInLeftSubtree(BinaryTreeNode<V> tree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTreeNode<V> leftmostNodeInRightSubtree(BinaryTreeNode<V> tree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTreeNode<V> invert(BinaryTreeNode<V> tree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTreeNode<V> clone(BinaryTreeNode<V> tree) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(BinaryTreeNode<V> tree, Comparator<V> 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;
|
||||
}
|
||||
}
|
36
src/aud/exam/prep/tree/Stack.java
Normal file
36
src/aud/exam/prep/tree/Stack.java
Normal file
@ -0,0 +1,36 @@
|
||||
package aud.exam.prep.tree;
|
||||
|
||||
import aud.exam.prep.array.Arrays;
|
||||
|
||||
@SuppressWarnings("ManualArrayCopy")
|
||||
public class Stack<T> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<BinaryTreeNode<Integer>> {
|
||||
|
||||
protected OrderedBinaryTreeNodeProcessorTest(OrderedTreeProcessor<Integer, BinaryTreeNode<Integer>> processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfNull() {
|
||||
assertTrue(processor.check(null, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfNoChildrenIsTrue() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 2;
|
||||
assertTrue(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfNoKeyIsFalse() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
assertFalse(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfLeftGreaterIsFalse() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.left = new BinaryTreeNode<>();
|
||||
t.left.key = 10;
|
||||
|
||||
assertFalse(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfLeftSmallerIsTrue() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.left = new BinaryTreeNode<>();
|
||||
t.left.key = 2;
|
||||
|
||||
assertTrue(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfRightGreaterIsTrue() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.right = new BinaryTreeNode<>();
|
||||
t.right.key = 10;
|
||||
|
||||
assertTrue(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfRightSmallerIsFalse() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.right = new BinaryTreeNode<>();
|
||||
t.right.key = 2;
|
||||
|
||||
assertFalse(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThat_checkOfBadRightIsFalse() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.right = new BinaryTreeNode<>();
|
||||
|
||||
assertFalse(processor.check(t, CMP));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testThat_checkOfLeftIsFalse() {
|
||||
var t = new BinaryTreeNode<Integer>();
|
||||
t.key = 5;
|
||||
|
||||
t.left = new BinaryTreeNode<>();
|
||||
|
||||
assertFalse(processor.check(t, CMP));
|
||||
}
|
||||
}
|
55
test/aud/exam/prep/tree/OrderedTreeProcessorTest.java
Normal file
55
test/aud/exam/prep/tree/OrderedTreeProcessorTest.java
Normal file
@ -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<T> {
|
||||
|
||||
protected final OrderedTreeProcessor<Integer, T> processor;
|
||||
|
||||
protected OrderedTreeProcessorTest(OrderedTreeProcessor<Integer, T> 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<Integer> 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));
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package aud.exam.prep.tree;
|
||||
|
||||
public class RecursiveOrderedBinaryTreeNodeProcessorTest extends OrderedBinaryTreeNodeProcessorTest {
|
||||
|
||||
public RecursiveOrderedBinaryTreeNodeProcessorTest() {
|
||||
super(new RecursiveOrderedBinaryTreeNodeProcessor<>());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user