diff --git a/pom.xml b/pom.xml
index 82b239e48..0347ccdba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,7 +38,9 @@
org.assertj
assertj-core
${assertj.version}
+ test
+
org.junit.jupiter
junit-jupiter-api
diff --git a/src/main/java/com/thealgorithms/datastructures/Node.java b/src/main/java/com/thealgorithms/datastructures/Node.java
new file mode 100644
index 000000000..c8d0e6cb4
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/Node.java
@@ -0,0 +1,32 @@
+package com.thealgorithms.datastructures;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Node {
+
+ private final T value;
+ private final List> children;
+
+ public Node(final T value) {
+ this.value = value;
+ this.children = new ArrayList<>();
+ }
+
+ public Node(final T value, final List> children) {
+ this.value = value;
+ this.children = children;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void addChild(Node child) {
+ children.add(child);
+ }
+
+ public List> getChildren() {
+ return children;
+ }
+}
diff --git a/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java b/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java
index 99ee6c27a..77043a03f 100644
--- a/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java
+++ b/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java
@@ -1,32 +1,45 @@
package com.thealgorithms.searches;
-import com.thealgorithms.searches.DepthFirstSearch.Node;
-import java.util.ArrayDeque;
-import java.util.Optional;
-import java.util.Queue;
+import com.thealgorithms.datastructures.Node;
+import java.util.*;
/**
* @author: caos321
* @date: 31 October 2021 (Sunday)
+ * @wiki: https://en.wikipedia.org/wiki/Breadth-first_search
*/
-public class BreadthFirstSearch {
- public static Optional search(final Node node, final String name) {
- if (node.getName().equals(name)) {
+public class BreadthFirstSearch {
+
+ private final List visited = new ArrayList<>();
+
+ public Optional> search(final Node node, final T value) {
+ if (node == null) {
+ return Optional.empty();
+ }
+ if (node.getValue().equals(value)) {
+ // add root node to visited
+ visited.add(value);
return Optional.of(node);
}
+ visited.add(node.getValue());
- Queue queue = new ArrayDeque<>(node.getSubNodes());
+ Queue> queue = new ArrayDeque<>(node.getChildren());
while (!queue.isEmpty()) {
- final Node current = queue.poll();
+ final Node current = queue.poll();
+ visited.add(current.getValue());
- if (current.getName().equals(name)) {
+ if (current.getValue().equals(value)) {
return Optional.of(current);
}
- queue.addAll(current.getSubNodes());
+ queue.addAll(current.getChildren());
}
return Optional.empty();
}
+
+ public List getVisited() {
+ return visited;
+ }
}
diff --git a/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java b/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java
index 3a12537ad..781768d80 100644
--- a/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java
+++ b/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java
@@ -1,79 +1,32 @@
package com.thealgorithms.searches;
+import com.thealgorithms.datastructures.Node;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
/**
* @author: caos321
* @date: 31 October 2021 (Sunday)
+ * @wiki: https://en.wikipedia.org/wiki/Depth-first_search
*/
-public class DepthFirstSearch {
+public class DepthFirstSearch {
- static class Node {
+ private final List visited = new ArrayList<>();
- private final String name;
- private final List subNodes;
-
- public Node(final String name) {
- this.name = name;
- this.subNodes = new ArrayList<>();
+ public Optional> recursiveSearch(final Node node, final Integer value) {
+ if (node == null) {
+ return Optional.empty();
}
-
- public Node(final String name, final List subNodes) {
- this.name = name;
- this.subNodes = subNodes;
- }
-
- public String getName() {
- return name;
- }
-
- public List getSubNodes() {
- return subNodes;
- }
- }
-
- public static Optional search(final Node node, final String name) {
- if (node.getName().equals(name)) {
+ visited.add(node.getValue());
+ if (node.getValue().equals(value)) {
return Optional.of(node);
}
- return node.getSubNodes().stream().map(value -> search(value, name)).flatMap(Optional::stream).findAny();
+ return node.getChildren().stream().map(v -> recursiveSearch(v, value)).flatMap(Optional::stream).findAny();
}
- public static void assertThat(final Object actual, final Object expected) {
- if (!Objects.equals(actual, expected)) {
- throw new AssertionError(String.format("expected=%s but was actual=%s", expected, actual));
- }
- }
-
- public static void main(final String[] args) {
- final Node rootNode = new Node("A", List.of(new Node("B", List.of(new Node("D"), new Node("F", List.of(new Node("H"), new Node("I"))))), new Node("C", List.of(new Node("G"))), new Node("E")));
-
- {
- final String expected = "I";
-
- final Node result = search(rootNode, expected).orElseThrow(() -> new AssertionError("Node not found!"));
-
- assertThat(result.getName(), expected);
- }
-
- {
- final String expected = "G";
-
- final Node result = search(rootNode, expected).orElseThrow(() -> new AssertionError("Node not found!"));
-
- assertThat(result.getName(), expected);
- }
-
- {
- final String expected = "E";
-
- final Node result = search(rootNode, expected).orElseThrow(() -> new AssertionError("Node not found!"));
-
- assertThat(result.getName(), expected);
- }
+ public List getVisited() {
+ return visited;
}
}
diff --git a/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java b/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java
index 2a5fe0b92..4c9f16d4c 100644
--- a/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java
+++ b/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java
@@ -2,33 +2,101 @@ package com.thealgorithms.searches;
import static org.junit.jupiter.api.Assertions.*;
+import com.thealgorithms.datastructures.Node;
import java.util.List;
import java.util.Optional;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-class BreadthFirstSearchTest {
+public class BreadthFirstSearchTest {
+ private Node root;
+ private BreadthFirstSearch bfs;
- private static final DepthFirstSearch.Node rootNode = new DepthFirstSearch.Node("A",
- List.of(new DepthFirstSearch.Node("B", List.of(new DepthFirstSearch.Node("D"), new DepthFirstSearch.Node("F", List.of(new DepthFirstSearch.Node("H"), new DepthFirstSearch.Node("I"))))), new DepthFirstSearch.Node("C", List.of(new DepthFirstSearch.Node("G"))), new DepthFirstSearch.Node("E")));
+ // Tree structure:
+ //
+ // A
+ // / | \
+ // B C D
+ // / \
+ // E F
- @Test
- void searchI() {
- Optional Inode = BreadthFirstSearch.search(rootNode, "I");
- assertTrue(Inode.isPresent());
- assertEquals(Inode.get().getName(), "I");
+ @BeforeEach
+ public void setUp() {
+ // nodes declaration
+ root = new Node<>("A");
+
+ var nodeB = new Node<>("B");
+ var nodeC = new Node<>("C");
+ var nodeD = new Node<>("D");
+
+ var nodeE = new Node<>("E");
+ var nodeF = new Node<>("F");
+
+ // tree initialization
+ root.addChild(nodeB);
+ root.addChild(nodeC);
+ root.addChild(nodeD);
+
+ nodeB.addChild(nodeE);
+ nodeB.addChild(nodeF);
+
+ // create an instance to monitor the visited nodes
+ bfs = new BreadthFirstSearch<>();
}
@Test
- void searchG() {
- Optional Gnode = BreadthFirstSearch.search(rootNode, "G");
- assertTrue(Gnode.isPresent());
- assertEquals(Gnode.get().getName(), "G");
+ public void testSearchRoot() {
+ String expectedValue = "A";
+ List expectedPath = List.of("A");
+
+ // check value
+ Optional> value = bfs.search(root, expectedValue);
+ assertEquals(expectedValue, value.orElse(new Node<>("")).getValue());
+
+ // check path
+ assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray());
}
@Test
- void searchE() {
- Optional Enode = BreadthFirstSearch.search(rootNode, "E");
- assertTrue(Enode.isPresent());
- assertEquals(Enode.get().getName(), "E");
+ public void testSearchF() {
+ String expectedValue = "F";
+ List expectedPath = List.of("A", "B", "C", "D", "E", "F");
+
+ // check value
+ Optional> value = Optional.of(bfs.search(root, expectedValue).orElse(new Node<>(null)));
+ assertEquals(expectedValue, value.get().getValue());
+
+ // check path
+ assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray());
+ }
+
+ @Test
+ void testSearchNull() {
+ List expectedPath = List.of("A", "B", "C", "D", "E", "F");
+ Optional> node = bfs.search(root, null);
+
+ // check value
+ assertTrue(node.isEmpty());
+
+ // check path
+ assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray());
+ }
+
+ @Test
+ void testNullRoot() {
+ var value = bfs.search(null, "B");
+ assertTrue(value.isEmpty());
+ }
+
+ @Test
+ void testSearchValueThatNotExists() {
+ List expectedPath = List.of("A", "B", "C", "D", "E", "F");
+ var value = bfs.search(root, "Z");
+
+ // check that the value is empty because it's not exists in the tree
+ assertTrue(value.isEmpty());
+
+ // check path is the whole list
+ assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray());
}
}
\ No newline at end of file
diff --git a/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java b/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java
new file mode 100644
index 000000000..f85e94090
--- /dev/null
+++ b/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java
@@ -0,0 +1,91 @@
+package com.thealgorithms.searches;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.thealgorithms.datastructures.Node;
+import java.util.List;
+import java.util.Optional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class DepthFirstSearchTest {
+
+ private Node root;
+ private DepthFirstSearch dfs;
+
+ //
+ // Tree structure:
+ // 1
+ // / | \
+ // 2 3 4
+ // / \
+ // 5 6
+
+ @BeforeEach
+ public void setUp() {
+ // nodes declaration
+ root = new Node<>(1);
+
+ var nodeB = new Node<>(2);
+ var nodeC = new Node<>(3);
+ var nodeD = new Node<>(4);
+
+ var nodeE = new Node<>(5);
+ var nodeF = new Node<>(6);
+
+ // tree initialization
+ root.addChild(nodeB);
+ root.addChild(nodeC);
+ root.addChild(nodeD);
+
+ nodeB.addChild(nodeE);
+ nodeB.addChild(nodeF);
+
+ // create an instance to monitor the visited nodes
+ dfs = new DepthFirstSearch<>();
+ }
+
+ @Test
+ public void testSearchRoot() {
+ Integer expectedValue = 1;
+ List expectedPath = List.of(1);
+
+ // check value
+ Optional> value = dfs.recursiveSearch(root, expectedValue);
+ assertEquals(expectedValue, value.orElse(new Node<>(null)).getValue());
+
+ // check path
+ assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray());
+ }
+
+ @Test
+ public void testSearch4() {
+ Integer expectedValue = 4;
+ List expectedPath = List.of(1, 2, 5, 6, 3, 4);
+
+ // check value
+ Optional> value = dfs.recursiveSearch(root, expectedValue);
+ assertEquals(expectedValue, value.orElse(new Node<>(null)).getValue());
+
+ // check path
+ assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray());
+ }
+
+ @Test
+ void testNullRoot() {
+ var value = dfs.recursiveSearch(null, 4);
+ assertTrue(value.isEmpty());
+ }
+
+ @Test
+ void testSearchValueThatNotExists() {
+ List expectedPath = List.of(1, 2, 5, 6, 3, 4);
+ var value = dfs.recursiveSearch(root, 10);
+
+ // check that the value is empty because it's not exists in the tree
+ assertTrue(value.isEmpty());
+
+ // check path is the whole list
+ assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray());
+ }
+}