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