diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java new file mode 100644 index 0000000000..9d301f9578 --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java @@ -0,0 +1,54 @@ +package com.baeldung.algorithms.breadthfirstsearch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class BreadthFirstSearchAlgorithm { + + private static final Logger LOGGER = LoggerFactory.getLogger(BreadthFirstSearchAlgorithm.class); + + public static Optional> search(T value, Tree root) { + Queue> queue = new ArrayDeque<>(); + queue.add(root); + + Tree currentNode; + while (!queue.isEmpty()) { + currentNode = queue.remove(); + LOGGER.info("Visited node with value: {}", currentNode.getValue()); + + if (currentNode.getValue().equals(value)) { + return Optional.of(currentNode); + } else { + queue.addAll(currentNode.getChildren()); + } + } + + return Optional.empty(); + } + + public static Optional> search(T value, Node start) { + Queue> queue = new ArrayDeque<>(); + queue.add(start); + + Node currentNode; + Set> alreadyVisited = new HashSet<>(); + + while (!queue.isEmpty()) { + currentNode = queue.remove(); + LOGGER.info("Visited node with value: {}", currentNode.getValue()); + + if (currentNode.getValue().equals(value)) { + return Optional.of(currentNode); + } else { + alreadyVisited.add(currentNode); + queue.addAll(currentNode.getNeighbors()); + queue.removeAll(alreadyVisited); + } + } + + return Optional.empty(); + } + +} diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java new file mode 100644 index 0000000000..54a589ae26 --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java @@ -0,0 +1,31 @@ +package com.baeldung.algorithms.breadthfirstsearch; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class Node { + + private T value; + private Set> neighbors; + + public Node(T value) { + this.value = value; + this.neighbors = new HashSet<>(); + } + + public T getValue() { + return value; + } + + public Set> getNeighbors() { + return Collections.unmodifiableSet(neighbors); + } + + public void connect(Node node) { + if (this == node) throw new IllegalArgumentException("Can't connect node to itself"); + this.neighbors.add(node); + node.neighbors.add(this); + } + +} diff --git a/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java new file mode 100644 index 0000000000..842d2837ff --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java @@ -0,0 +1,34 @@ +package com.baeldung.algorithms.breadthfirstsearch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Tree { + + private T value; + private List> children; + + private Tree(T value) { + this.value = value; + this.children = new ArrayList<>(); + } + + public static Tree of(T value) { + return new Tree<>(value); + } + + public T getValue() { + return value; + } + + public List> getChildren() { + return Collections.unmodifiableList(children); + } + + public Tree addChild(T value) { + Tree newChild = new Tree<>(value); + children.add(newChild); + return newChild; + } +} diff --git a/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java new file mode 100644 index 0000000000..aa22fc5353 --- /dev/null +++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java @@ -0,0 +1,86 @@ +package com.baeldung.algorithms.breadthfirstsearch; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class BreadthFirstSearchAlgorithmUnitTest { + + private Tree root; + private Tree rootFirstChild; + private Tree depthMostChild; + private Tree rootSecondChild; + + private Node start; + private Node firstNeighbor; + private Node firstNeighborNeighbor; + private Node secondNeighbor; + + @Test + void givenTree_whenSearchTen_thenRoot() { + initTree(); + assertThat(BreadthFirstSearchAlgorithm.search(10, root)).isPresent().contains(root); + } + + @Test + void givenTree_whenSearchThree_thenDepthMostValue() { + initTree(); + assertThat(BreadthFirstSearchAlgorithm.search(3, root)).isPresent().contains(depthMostChild); + } + + @Test + void givenTree_whenSearchFour_thenRootSecondChild() { + initTree(); + assertThat(BreadthFirstSearchAlgorithm.search(4, root)).isPresent().contains(rootSecondChild); + } + + @Test + void givenTree_whenSearchFive_thenNotFound() { + initTree(); + assertThat(BreadthFirstSearchAlgorithm.search(5, root)).isEmpty(); + } + + private void initTree() { + root = Tree.of(10); + rootFirstChild = root.addChild(2); + depthMostChild = rootFirstChild.addChild(3); + rootSecondChild = root.addChild(4); + } + + @Test + void givenNode_whenSearchTen_thenStart() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(10, firstNeighborNeighbor)).isPresent().contains(start); + } + + @Test + void givenNode_whenSearchThree_thenNeighborNeighbor() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(3, firstNeighborNeighbor)).isPresent().contains(firstNeighborNeighbor); + } + + @Test + void givenNode_whenSearchFour_thenSecondNeighbor() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(4, firstNeighborNeighbor)).isPresent().contains(secondNeighbor); + } + + @Test + void givenNode_whenSearchFive_thenNotFound() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(5, firstNeighborNeighbor)).isEmpty(); + } + + private void initNode() { + start = new Node<>(10); + firstNeighbor = new Node<>(2); + start.connect(firstNeighbor); + + firstNeighborNeighbor = new Node<>(3); + firstNeighbor.connect(firstNeighborNeighbor); + firstNeighborNeighbor.connect(start); + + secondNeighbor = new Node<>(4); + start.connect(secondNeighbor); + } +} \ No newline at end of file