Partly implement RecursiveOrderedBinaryTreeNodeProcessor
This commit is contained in:
		
							parent
							
								
									5661e3dbd2
								
							
						
					
					
						commit
						268361484d
					
				
					 8 changed files with 501 additions and 0 deletions
				
			
		
							
								
								
									
										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 a new issue