Skip to content
  • Sponsor bobocode-projects/java-fundamentals-exercises

  • Notifications You must be signed in to change notification settings
  • Fork 495

Y.Fomin - Red Black Tree Implementation #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: completed
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>2-2-6-binary-search-tree</artifactId>
<artifactId>2-2-6-trees</artifactId>


</project>
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

import java.util.function.Consumer;

public interface BinarySearchTree<T extends Comparable<T>> {
public interface Tree<T extends Comparable<T>> {
/**
* insert an element
* @return true if element did not exist in the tree and was inserted successfully
@@ -29,4 +29,6 @@ public interface BinarySearchTree<T extends Comparable<T>> {
* @param consumer accepts ref. to node during traversing
*/
void inOrderTraversal(Consumer<T> consumer);

void preOrderTraversal(Consumer<T> consumer);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.bobocode.cs;
package com.bobocode.cs.binary;

import com.bobocode.cs.Tree;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
* {@link RecursiveBinarySearchTree} is an implementation of a {@link BinarySearchTree} that is based on a linked nodes
* {@link BinarySearchTree} is an implementation of a {@link Tree} that is based on a linked nodes
* and recursion. A tree node is represented as a nested class {@link Node}. It holds an element (a value) and
* two references to the left and right child nodes.
* <p><p>
@@ -16,14 +17,14 @@
* @author Taras Boychuk
* @author Maksym Stasiuk
*/
public class RecursiveBinarySearchTree<T extends Comparable<T>> implements BinarySearchTree<T> {
public class BinarySearchTree<T extends Comparable<T>> implements Tree<T> {
private static class Node<T> {
T element;
T value;
Node<T> left;
Node<T> right;

private Node(T element) {
this.element = element;
private Node(T value) {
this.value = value;
}

public static <T> Node<T> valueOf(T element) {
@@ -34,8 +35,8 @@ public static <T> Node<T> valueOf(T element) {
private Node<T> root;
private int size = 0;

public static <T extends Comparable<T>> RecursiveBinarySearchTree<T> of(T... elements) {
RecursiveBinarySearchTree<T> bst = new RecursiveBinarySearchTree<>();
public static <T extends Comparable<T>> BinarySearchTree<T> of(T... elements) {
BinarySearchTree<T> bst = new BinarySearchTree<>();
Stream.of(elements).forEach(bst::insert);
return bst;
}
@@ -60,9 +61,9 @@ boolean insertElement(T element) {
}

private boolean insertIntoSubTree(Node<T> subTreeRoot, T element) {
if (subTreeRoot.element.compareTo(element) > 0) {
if (subTreeRoot.value.compareTo(element) > 0) {
return insertIntoLeftSubtree(subTreeRoot, element);
} else if (subTreeRoot.element.compareTo(element) < 0) {
} else if (subTreeRoot.value.compareTo(element) < 0) {
return insertIntoRightSubtree(subTreeRoot, element);
} else {
return false;
@@ -97,9 +98,9 @@ public boolean contains(T element) {
private Node<T> findChildNodeByElement(Node<T> node, T element) {
if (node == null) {
return null;
} else if (node.element.compareTo(element) > 0) {
} else if (node.value.compareTo(element) > 0) {
return findChildNodeByElement(node.left, element);
} else if (node.element.compareTo(element) < 0) {
} else if (node.value.compareTo(element) < 0) {
return findChildNodeByElement(node.right, element);
} else {
return node;
@@ -129,10 +130,21 @@ public void inOrderTraversal(Consumer<T> consumer) {
inOrderTraversal(root, consumer);
}

@Override
public void preOrderTraversal(Consumer<T> consumer) {
preOrderTraversal(root, consumer);
}
private void preOrderTraversal(Node<T> node, Consumer<T> consumer){
if (node != null){
consumer.accept(node.value);
preOrderTraversal(node.left, consumer);
preOrderTraversal(node.right, consumer);
}
}
private void inOrderTraversal(Node<T> node, Consumer<T> consumer) {
if (node != null) {
inOrderTraversal(node.left, consumer);
consumer.accept(node.element);
consumer.accept(node.value);
inOrderTraversal(node.right, consumer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
package com.bobocode.cs.redblack;

import com.bobocode.cs.Tree;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
* {@link RedBlackSearchTree} is an implementation of a {@link Tree} that is based on a linked nodes
* and recursion. A tree node is represented as a nested class {@link RedBlackSearchTree.Node}. It holds an element (a value),
* three references to the left, right and parent nodes. Also, {@link RedBlackSearchTree.Node} has a boolean color field where RED is true and BLACK is false.
* To understand Red Black tree better following resources can be used
* <ul>
* <li> <a href="https://yongdanielliang.github.io/animation/web/RBTree.html">Red Black Tree visualizer</a></strong></li>
* <li> <a href="https://www.youtube.com/watch?v=qvZGUFHWChY&list=PL9xmBV_5YoZNqDI8qfOZgzbqahCUmUEin&pp=iAQB">Red Black Tree tutorial videos</a></strong></li>
* </ul>
*
* @param <T> a type of elements that are stored in the tree
* @author Taras Boychuk
* @author Yuriy Fomin
*/
public class RedBlackSearchTree<T extends Comparable<T>> implements Tree<T> {

private static final boolean RED = true;
private static final boolean BLACK = false;

private int size;
private Node<T> root;

public static <T extends Comparable<T>> RedBlackSearchTree<T> of(T... elements) {
RedBlackSearchTree<T> redBlackSearchTree = new RedBlackSearchTree<>();
Stream.of(elements).forEach(redBlackSearchTree::insert);
return redBlackSearchTree;
}

@Override
public boolean insert(@NonNull T element) {
Node<T> newNode = insertHelper(this.root, element);
if (newNode != null) {
this.size += 1;
repair(newNode);
}
this.root.setColor(BLACK);
return newNode != null;
}

private Node<T> insertHelper(Node<T> node, T value) {
if (this.root == null) {
this.root = new Node<>(value, null);
return this.root;
}
if (node == null) {
return null;
}
int res = value.compareTo(node.getValue());
if (res < 0 && node.getLeft() == null) {
node.setLeft(new Node<>(value, node));
return node.getLeft();
}
if (res > 0 && node.getRight() == null) {
node.setRight(new Node<>(value, node));
return node.getRight();
}

if (res < 0) {
return insertHelper(node.getLeft(), value);
}
if (res > 0) {
return insertHelper(node.getRight(), value);
}
return null;
}

private void repair(Node<T> node) {
if (node == null) {
return;
}
if (node.color == BLACK){
return;
}
if (node.getParent() == null) {
return;
}
if (node.getParent().color == BLACK) {
return;
}
if (node.getGrandParent() == null) {
return;
}
Node<T> grandParent = node.getGrandParent();
Node<T> parent = node.getParent();
Node<T> uncle = getUncle(parent);
// case 1 uncle is red -> recolor
boolean uncleColorIsRed = uncle != null && uncle.color == RED;
if (uncleColorIsRed) {
parent.setColor(BLACK);
grandParent.setColor(RED);
uncle.setColor(BLACK);
repair(node.getGrandParent());
return;
}
// case 2 uncle is BLACK and right triangle is formed -> rotate to right parent
boolean uncleColorIsBlack = uncle == null || uncle.color == BLACK;
if (uncleColorIsBlack && childAndParentFormTriangle(parent, node) && parent.getLeft() == node) {
rotateRight(parent);
repair(parent);
return;
}
// case 2 uncle is BLACK and left triangle is formed -> rotate to left parent
if (uncleColorIsBlack && childAndParentFormTriangle(parent, node) && parent.getRight() == node) {
rotateLeft(parent);
repair(parent);
return;
}
// case 3 uncle is BLACK and flat line is formed to the right -> rotate grandparent to right and recolor parent and grandparent
if (uncleColorIsBlack && childAndParentFormFlatLine(parent, node) && parent.getLeft() == node) {
rotateRight(grandParent);
grandParent.setColor(RED);
parent.setColor(BLACK);
repair(node.getParent());
return;
}

// case 3 uncle is BLACK and flat line is formed to the left -> rotate grandparent to right and recolor parent and grandparent
if (uncleColorIsBlack && childAndParentFormFlatLine(parent, node) && parent.getRight() == node) {
rotateLeft(grandParent);
grandParent.setColor(RED);
parent.setColor(BLACK);
repair(node.getParent());
return;
}
repair(node.getParent());
}

private boolean childAndParentFormTriangle(Node<T> parent, Node<T> child) {
if (parent == null) {
return false;
}
if (child == null) {
return false;
}
if (parent.getParent() == null) {
return false;
}
Node<T> grandParent = parent.getParent();
if (parent.getLeft() == child && grandParent.getRight() == parent) {
return true;
}
return parent.getRight() == child && grandParent.getLeft() == parent;
}

private boolean childAndParentFormFlatLine(Node<T> parent, Node<T> child) {
if (parent == null) {
return false;
}
if (child == null) {
return false;
}
if (parent.getParent() == null) {
return false;
}
Node<T> grandParent = parent.getParent();
if (grandParent.getRight() == parent && parent.getRight() == child) {
return true;
}
return grandParent.getLeft() == parent && parent.getLeft() == child;
}

private void rotateRight(@NonNull Node<T> node) {
Node<T> leftChild = node.getLeft();
Node<T> parent = node.getParent();
node.setLeft(null);
Node<T> leftRightChild = leftChild == null ? null : leftChild.getRight();
if (parent != null && parent.getRight() == node) {
parent.setRight(leftChild);
}

if (parent != null && parent.getLeft() == node) {
parent.setLeft(leftChild);
}

if (leftChild != null) {
leftChild.setRight(node);
leftChild.setParent(node.getParent());
}
if (node == this.root) {
this.root = leftChild;
}
node.setParent(leftChild);
node.setLeft(leftRightChild);
if (leftRightChild != null) {
leftRightChild.setParent(node);
}
}

private void rotateLeft(@NonNull Node<T> node) {
Node<T> rightChild = node.getRight();
Node<T> parent = node.getParent();
node.setRight(null);
Node<T> rightLeftChild = rightChild == null ? null : rightChild.getLeft();
if (parent != null && parent.getRight() == node) {
parent.setRight(rightChild);
}
if (parent != null && parent.getLeft() == node) {
parent.setLeft(rightChild);
}
if (rightChild != null) {
rightChild.setLeft(node);
rightChild.setParent(node.getParent());
}
if (node == this.root) {
this.root = rightChild;
}
node.setParent(rightChild);
node.setRight(rightLeftChild);
if (rightLeftChild != null) {
rightLeftChild.setParent(node);
}
}

private Node<T> getUncle(@NonNull Node<T> parent) {
Node<T> grandParent = parent.getParent();
if (grandParent.getLeft() == parent) {
return grandParent.getRight();
}
if (grandParent.getRight() == parent) {
return grandParent.getLeft();
}
throw new IllegalStateException("Parent is not a child of grandparent");
}

@Override
public boolean contains(@NonNull T element) {
return contains(this.root, element);
}

private boolean contains(Node<T> node, T el) {
if (node == null) {
return false;
}
int res = el.compareTo(node.getValue());
if (res == 0) {
return true;
}
if (res < 0) {
return contains(node.getLeft(), el);
}
return contains(node.getRight(), el);
}

@Override
public int size() {
return this.size;
}

@Override
public int depth() {
return root == null ? 0 : depthHelper(this.root) - 1;
}

int depthHelper(Node<T> node) {
if (node == null) {
return 0;
}
return 1 + Math.max(depthHelper(node.getLeft()), depthHelper(node.getRight()));
}

@Override
public void inOrderTraversal(Consumer<T> consumer) {
inOrderTraversal(root, consumer);
}

private void inOrderTraversal(Node<T> node, Consumer<T> consumer) {
if (node == null) {
return;
}
inOrderTraversal(node.getLeft(), consumer);
consumer.accept(node.getValue());
inOrderTraversal(node.getRight(), consumer);
}

@Override
public void preOrderTraversal(Consumer<T> consumer) {
preOrderTraversal(root, consumer);
}

private void preOrderTraversal(Node<T> node, Consumer<T> consumer) {
if (node == null) {
return;
}
consumer.accept(node.getValue());
preOrderTraversal(node.getLeft(), consumer);
preOrderTraversal(node.getRight(), consumer);
}

@Data
private static class Node<V> {
V value;
boolean color;
@ToString.Exclude
Node<V> parent;
Node<V> left;
Node<V> right;

private Node(V value, Node<V> parent) {
this.value = value;
this.parent = parent;
this.color = RED;
}

private Node<V> getGrandParent() {
if (this.parent == null) {
return null;
}
return this.getParent().getParent();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.bobocode.cs;

import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThat;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public abstract class AbstractTreeUnitTest {

protected static final Integer[] someElements = {10, 9, 11, 8, 12, 7};

protected static final Predicate<Field> SIZE_FIELD = field ->
field.getName().toLowerCase().contains("size") || field.getName().toLowerCase().contains("length");

protected static final Predicate<Field> NODE_FIELD = field ->
field.getType().getSimpleName().equals("Node");

protected static final Predicate<Field> ELEMENT_FIELD = field -> field.getName().toLowerCase().contains("value");

protected static final Predicate<Field> LEFT_FIELD = field ->
field.getName().toLowerCase().contains("left")
&& field.getType().getSimpleName().equals("Node");

protected static final Predicate<Field> RIGHT_FIELD = field ->
field.getName().toLowerCase().contains("right")
&& field.getType().getSimpleName().equals("Node");

protected static Class<?> getInnerClass(Class<?> treeClazz) {
return Arrays.stream(treeClazz.getDeclaredClasses())
.filter(Class::isMemberClass)
.findAny()
.orElseThrow();
}

@SneakyThrows
protected int getInnerSize(Tree<?> tree) {
return (int) getInnerSizeField(tree.getClass()).get(tree);
}

protected Field getInnerSizeField(Class<?> treeClazz) {
Field sizeField = Arrays.stream(treeClazz.getDeclaredFields())
.filter(SIZE_FIELD)
.findAny()
.orElseThrow();
sizeField.setAccessible(true);
return sizeField;
}

protected void checkTreeNode(Class<?> treeClazz) {
Class<?> innerClass = getInnerClass(treeClazz);
String name = innerClass.getSimpleName();

assertThat(name).isEqualTo("Node");
}

protected void checkTreeFieldsCheck(Class<?> treeClazz) {
boolean hasSizeField = Arrays.stream(treeClazz.getDeclaredFields())
.anyMatch(SIZE_FIELD);

boolean hasNodeField = Arrays.stream(treeClazz.getDeclaredFields())
.anyMatch(NODE_FIELD);

assertThat(hasSizeField).isTrue();
assertThat(hasNodeField).isTrue();
}

protected void commonNodeFieldsCheck(Class<?> treeClazz) {
Class<?> innerClass = getInnerClass(treeClazz);

boolean isElement = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(ELEMENT_FIELD);

boolean isLeft = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(LEFT_FIELD);

boolean isRight = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(RIGHT_FIELD);

assertThat(isElement).isTrue();
assertThat(isLeft).isTrue();
assertThat(isRight).isTrue();
}

protected void testOfMethod(Tree<Integer> tree) {
for (var e : AbstractTreeUnitTest.someElements) {
assertThat(contains(getRootObject(tree), e)).isTrue();
}
assertThat(getInnerSize(tree)).isEqualTo(AbstractTreeUnitTest.someElements.length);
}

protected void testInsertMethod(Tree<Integer> tree) {
for (Integer e : AbstractTreeUnitTest.someElements) {
assertThat(contains(getRootObject(tree), e)).isFalse(); //does not contain
assertThat(tree.insert(e)).isTrue(); //do insert
assertThat(contains(getRootObject(tree), e)).isTrue(); //and contains
}
}

protected Object findNodeByElement(Object node, int element) {
if (node == null) {
return null;
}
if (element == getElement(node)) {
return node;
} else if (element < getElement(node)) {
return findNodeByElement(getLeftNode(node), element);
} else if (element > getElement(node)) {
return findNodeByElement(getRightNode(node), element);
} else {
return node;
}
}

@SneakyThrows
protected Object getRootObject(Tree<?> tree) {
Field nodeField = Arrays.stream(tree.getClass().getDeclaredFields())
.filter(NODE_FIELD)
.findAny()
.orElseThrow();
nodeField.setAccessible(true);
return nodeField.get(tree);
}

@SneakyThrows
protected boolean contains(Object node, int element) {
return findNodeByElement(node, element) != null;
}

@SneakyThrows
protected int getElement(Object node) {
return (int) getNodesField(node, ELEMENT_FIELD).get(node);
}

@SneakyThrows
protected Object getLeftNode(Object node) {
return getNodesField(node, LEFT_FIELD).get(node);
}

@SneakyThrows
protected Object getRightNode(Object node) {
return getNodesField(node, RIGHT_FIELD).get(node);
}

@SneakyThrows
protected Field getNodesField(Object node, Predicate<Field> option) {
Field field = Arrays.stream(node.getClass().getDeclaredFields())
.filter(option)
.findAny()
.orElseThrow();
field.setAccessible(true);
return field;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bobocode.cs;
package com.bobocode.cs.binary;

import com.bobocode.cs.AbstractTreeUnitTest;
import com.bobocode.cs.Tree;
import lombok.SneakyThrows;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
@@ -8,13 +10,11 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
@@ -30,99 +30,47 @@
* @author Maksym Stasiuk
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class RecursiveBinarySearchTreeTest {
private static final Predicate<Field> SIZE_FIELD = field ->
field.getName().toLowerCase().contains("size") || field.getName().toLowerCase().contains("length");

private static final Predicate<Field> NODE_FIELD = field ->
field.getType().getSimpleName().equals("Node");

private static final Predicate<Field> ELEMENT_FIELD = field ->
field.getName().toLowerCase().contains("element")
|| field.getName().toLowerCase().contains("item")
|| field.getName().toLowerCase().contains("value");

private static final Predicate<Field> LEFT_FIELD = field ->
field.getName().toLowerCase().contains("left")
&& field.getType().getSimpleName().equals("Node");

private static final Predicate<Field> RIGHT_FIELD = field ->
field.getName().toLowerCase().contains("right")
&& field.getType().getSimpleName().equals("Node");

private static final Integer[] someElements = {10, 9, 11, 8, 12, 7};

private BinarySearchTree<Integer> tree = new RecursiveBinarySearchTree<>();
class BinarySearchTreeTest extends AbstractTreeUnitTest {

private Tree<Integer> tree = new BinarySearchTree<>();

@Test
@Order(1)
void properNodeClassNameCheck() {
Class<?> innerClass = getInnerClass();
String name = innerClass.getSimpleName();

assertThat(name).isEqualTo("Node");
checkTreeNode(BinarySearchTree.class);
}

@Test
@Order(2)
void properTreeFieldsCheck() {
Class<?> treeClass = tree.getClass();

boolean hasSizeField = Arrays.stream(treeClass.getDeclaredFields())
.anyMatch(SIZE_FIELD);

boolean hasNodeField = Arrays.stream(treeClass.getDeclaredFields())
.anyMatch(NODE_FIELD);

assertThat(hasSizeField).isTrue();
assertThat(hasNodeField).isTrue();
checkTreeFieldsCheck(BinarySearchTree.class);
}

@Test
@Order(3)
void properNodeFieldsCheck() {
Class<?> innerClass = getInnerClass();

boolean isElement = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(ELEMENT_FIELD);

boolean isLeft = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(LEFT_FIELD);

boolean isRight = Arrays.stream(innerClass.getDeclaredFields())
.anyMatch(RIGHT_FIELD);

assertThat(isElement).isTrue();
assertThat(isLeft).isTrue();
assertThat(isRight).isTrue();
commonNodeFieldsCheck(BinarySearchTree.class);
}

@Test
@Order(4)
void of() {
tree = RecursiveBinarySearchTree.of(someElements);
for (var e : someElements) {
assertThat(contains(getRootObject(), e)).isTrue();
}
assertThat(getInnerSize()).isEqualTo(someElements.length);
tree = BinarySearchTree.of(someElements);
testOfMethod(tree);
}

@Test
@Order(5)
void insert() {
for (Integer e : someElements) {
assertThat(contains(getRootObject(), e)).isFalse(); //does not contain
assertThat(tree.insert(e)).isTrue(); //do insert
assertThat(contains(getRootObject(), e)).isTrue(); //and contains
}
testInsertMethod(tree);
}

@Test
@Order(6)
void insertToRootIfTreeIsEmpty() {
assertThat(getRootObject()).isNull();
assertThat(getRootObject(tree)).isNull();
tree.insert(10);
assertThat(getRootObject()).isNotNull();
assertThat(getRootObject(tree)).isNotNull();
}

@Test
@@ -131,7 +79,7 @@ void insertLeftIfLessThanRoot() {
tree.insert(10); //root
tree.insert(5); //left

assertThat(getLeftNode(getRootObject())).isNotNull();
assertThat(getLeftNode(getRootObject(tree))).isNotNull();
}

@Test
@@ -140,7 +88,7 @@ void insertRightIfGreaterThanRoot() {
tree.insert(10); //root
tree.insert(15); //right

assertThat(getRightNode(getRootObject())).isNotNull();
assertThat(getRightNode(getRootObject(tree))).isNotNull();
}

@Test
@@ -192,21 +140,21 @@ void sizeIsGrowingWhenInserting() {
tree.insert(15);
tree.insert(20);

assertThat(getInnerSize()).isEqualTo(3);
assertThat(getInnerSize(tree)).isEqualTo(3);
}

@Test
@Order(15)
void sizeDoesNotGrowWhenInsertingNotUnique() {
fillTestTree(10, 11, 12);

assertThat(getInnerSize()).isEqualTo(3);
assertThat(getInnerSize(tree)).isEqualTo(3);

tree.insert(10);
tree.insert(11);
tree.insert(12);

assertThat(getInnerSize()).isEqualTo(3);
assertThat(getInnerSize(tree)).isEqualTo(3);
}

@Order(16)
@@ -243,12 +191,24 @@ void inorderTraversal() {
Integer[] sortedElements = Arrays.copyOf(someElements, someElements.length);
Arrays.sort(sortedElements);

List<Integer> traversedElements = new ArrayList<>(getInnerSize());
List<Integer> traversedElements = new ArrayList<>(getInnerSize(tree));
tree.inOrderTraversal(traversedElements::add);

assertThat(traversedElements).isEqualTo(List.of(sortedElements));
}

@Test
@Order(20)
void preOrderTraversal() {
fillTestTree(someElements);
Integer[] preOrderOfSomeElements = {10, 9, 8, 7, 11, 12};

List<Integer> traversedElements = new ArrayList<>(getInnerSize(tree));
tree.preOrderTraversal(traversedElements::add);

assertThat(traversedElements).isEqualTo(List.of(preOrderOfSomeElements));
}

public static Stream<Arguments> depthArguments() {
return Stream.of(
//empty tree
@@ -289,28 +249,6 @@ public static Stream<Arguments> depthArguments() {
arguments(new Integer[]{6, 2, 7, 1, 5, 8, 4, 9, 3}, 4));
}


@SneakyThrows
private int getInnerSize() {
return (int) getInnerSizeField().get(tree);
}

private Field getInnerSizeField() {
Field sizeField = Arrays.stream(tree.getClass().getDeclaredFields())
.filter(SIZE_FIELD)
.findAny()
.orElseThrow();
sizeField.setAccessible(true);
return sizeField;
}

private Class<?> getInnerClass() {
return Arrays.stream(tree.getClass().getDeclaredClasses())
.filter(Class::isMemberClass)
.findAny()
.orElseThrow();
}

@SneakyThrows
private Field getRootField() {
Field nodeField = Arrays.stream(tree.getClass().getDeclaredFields())
@@ -321,65 +259,13 @@ private Field getRootField() {
return nodeField;
}

@SneakyThrows
private Object getRootObject() {
Field nodeField = Arrays.stream(tree.getClass().getDeclaredFields())
.filter(NODE_FIELD)
.findAny()
.orElseThrow();
nodeField.setAccessible(true);
return nodeField.get(tree);
}

@SneakyThrows
private boolean contains(Object node, int element) {
return findNodeByElement(node, element) != null;
}

private Object findNodeByElement(Object node, int element) {
if (node == null) {
return null;
}
if (element == getElement(node)) {
return node;
} else if (element < getElement(node)) {
return findNodeByElement(getLeftNode(node), element);
} else if (element > getElement(node)) {
return findNodeByElement(getRightNode(node), element);
} else {
return node;
}
}

@SneakyThrows
private Field getNodesField(Object node, Predicate<Field> option) {
Field field = Arrays.stream(node.getClass().getDeclaredFields())
.filter(option)
.findAny()
.orElseThrow();
field.setAccessible(true);
return field;
}

@SneakyThrows
private int getElement(Object node) {
return (int) getNodesField(node, ELEMENT_FIELD).get(node);
}

@SneakyThrows
private Object getLeftNode(Object node) {
return getNodesField(node, LEFT_FIELD).get(node);
}

@SneakyThrows
private Object getRightNode(Object node) {
return getNodesField(node, RIGHT_FIELD).get(node);
}

@SneakyThrows
private Object newNode(int element) {
Object nodeInstance;
Constructor<?>[] constructors = getInnerClass().getDeclaredConstructors();
Constructor<?>[] constructors = getInnerClass(BinarySearchTree.class).getDeclaredConstructors();
Constructor<?> constructor;

constructor = constructors[0];
@@ -395,9 +281,9 @@ private Object newNode(int element) {
}

@SneakyThrows
private void insertElement(int element) {
private void insertElement(Tree<Integer> tree, int element) {

Object root = getRootObject();
Object root = getRootObject(tree);

if (root == null) {
getRootField().set(tree, newNode(element));
@@ -438,10 +324,10 @@ private boolean insertRight(Object node, int element) {

@SneakyThrows
private void fillTestTree(Integer... elements) {
tree = new RecursiveBinarySearchTree<>();
tree = new BinarySearchTree<>();
for (Integer e : elements) {
insertElement(e);
insertElement(tree, e);
}
getInnerSizeField().set(tree, elements.length);
getInnerSizeField(tree.getClass()).set(tree, elements.length);
}
}

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion 2-0-data-structures-and-algorithms/pom.xml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
<module>2-2-3-linked-queue</module>
<module>2-2-4-linked-list</module>
<module>2-2-5-array-list</module>
<module>2-2-6-binary-search-tree</module>
<module>2-2-6-trees</module>
<module>2-2-9-hash-table</module>
<module>data-structures-and-algorithms-util</module>
</modules>