diff --git a/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java b/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java index a6f643e..f8e3397 100644 --- a/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java +++ b/src/aud/exam/prep/tree/RecursiveOrderedBinaryTreeNodeProcessor.java @@ -1,5 +1,7 @@ package aud.exam.prep.tree; +import aud.exam.prep.Pointer; + import java.util.Comparator; public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTreeNodeProcessor { @@ -10,12 +12,12 @@ public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTre } @Override - public boolean override(BinaryTreeNode pointer, V from, V to, Comparator cmp) { + public boolean override(Pointer> pointer, V from, V to, Comparator cmp) { return false; } @Override - public boolean overrideAll(BinaryTreeNode pointer, V from, V to, Comparator cmp) { + public boolean overrideAll(Pointer> pointer, V from, V to, Comparator cmp) { return false; } @@ -29,7 +31,7 @@ public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTre var c = cmp.compare(v, tree.key); - if (c <= 0) { + if (c < 0 || c == 0 && tree.right != null) { tree.left = insert(tree.left, v, cmp); } else { tree.right = insert(tree.right, v, cmp); @@ -39,12 +41,83 @@ public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTre } @Override - public boolean remove(BinaryTreeNode tree, V v, Comparator cmp) { - return false; + public boolean remove(Pointer> pointer, V v, Comparator cmp) { + var tree = pointer.deref; + + if (tree == null) { + return false; + } + + var c = cmp.compare(v, tree.key); + + if (c == 0) { + if (tree.left == null) { + pointer.deref = tree.right; + } else if (tree.right == null) { + pointer.deref = tree.left; + } else { + tree.key = getReplacementForRemoval(tree.left, tree, false); + } + + return true; + } + + if (c < 0) { + return removeRec(tree.left, v, cmp, tree, false); + } + + return removeRec(tree.right, v, cmp, tree, true); + } + + private boolean removeRec(BinaryTreeNode node, V v, Comparator cmp, BinaryTreeNode prev, boolean right) { + if (node == null) { + return false; + } + + var c = cmp.compare(v, node.key); + + if (c == 0) { + if (node.left == null) { + if (right) { + prev.right = node.right; + } else { + prev.left = node.right; + } + } else if (node.right == null) { + if (right) { + prev.right = node.left; + } else { + prev.left = node.left; + } + } else { + node.key = getReplacementForRemoval(node.left, node, false); + } + + return true; + } + + if (c < 0) { + return removeRec(node.left, v, cmp, node, false); + } + + return removeRec(node.right, v, cmp, node, true); + } + + private V getReplacementForRemoval(BinaryTreeNode node, BinaryTreeNode prev, boolean right) { + if (node.right == null) { + if (right) { + prev.right = node.left; + } else { + prev.left = node.left; + } + return node.key; + } + + return getReplacementForRemoval(node.right, node, true); } @Override - public boolean removeAll(BinaryTreeNode tree, V v, Comparator cmp) { + public boolean removeAll(Pointer> tree, V v, Comparator cmp) { return false; } @@ -153,9 +226,7 @@ public class RecursiveOrderedBinaryTreeNodeProcessor extends OrderedBinaryTre } if (tree.right != null) { - if (!check(tree.right, cmp) || cmp.compare(tree.right.key, tree.key) < 0) { - return false; - } + return check(tree.right, cmp) && cmp.compare(tree.right.key, tree.key) >= 0; } return true; diff --git a/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java b/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java index bd1ea35..af413c0 100644 --- a/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java +++ b/test/aud/exam/prep/tree/OrderedTreeProcessorTest.java @@ -1,6 +1,7 @@ package aud.exam.prep.tree; import aud.exam.prep.ListProvider; +import aud.exam.prep.Pointer; import aud.exam.prep.Tests; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -80,6 +81,40 @@ public abstract class OrderedTreeProcessorTest { assertEquals(max, processor.secondMax(t)); } + @ParameterizedTest + @ArgumentsSource(ListProvider.class) + void testThat_removeOfNonExistsIsFalseAndDoesNotModify(List list) { + T t = asTree(list); + list.sort(CMP); + + var p = new Pointer<>(t); + assertFalse(processor.remove(p, -1, CMP)); + assertFalse(processor.remove(p, 999, CMP)); + t = p.deref; + + assertTrue(processor.check(t, CMP)); + assertIterableEquals(list, processor.iterate(t)); + } + + @ParameterizedTest + @ArgumentsSource(ListProvider.class) + void testThat_removeOfExistsIsTrueAndDoesModify(List list) { + T t = asTree(list); + list.sort(CMP); + + while (!list.isEmpty()) { + var toRemove = list.get(list.size()/2); + list.remove(toRemove); + + var p = new Pointer<>(t); + assertTrue(processor.remove(p, toRemove, CMP)); + t = p.deref; + + assertTrue(processor.check(t, CMP)); + assertIterableEquals(list, processor.iterate(t)); + } + } + @ParameterizedTest @ArgumentsSource(ListProvider.class)