Partly implement RecursiveOrderedBinaryTreeNodeProcessor

This commit is contained in:
2021-09-03 11:05:26 +02:00
parent 5661e3dbd2
commit 268361484d
8 changed files with 501 additions and 0 deletions

View 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;
}
}

View 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);
}
}

View 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);
}

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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));
}
}

View 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));
}
}

View File

@ -0,0 +1,8 @@
package aud.exam.prep.tree;
public class RecursiveOrderedBinaryTreeNodeProcessorTest extends OrderedBinaryTreeNodeProcessorTest {
public RecursiveOrderedBinaryTreeNodeProcessorTest() {
super(new RecursiveOrderedBinaryTreeNodeProcessor<>());
}
}