[BAEL-3128] Code for Breadth-First Search in Java Article
* Added Tree and algorithm tests for trees * Implemented algorithm for trees * Added algorithm for graphs * Bidirectional graph * Minor updates * Readibility changes
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
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 <T> Optional<Tree<T>> search(T value, Tree<T> root) {
|
||||
Queue<Tree<T>> queue = new ArrayDeque<>();
|
||||
queue.add(root);
|
||||
|
||||
return searchTreeQueue(value, queue);
|
||||
}
|
||||
|
||||
private static <T> Optional<Tree<T>> searchTreeQueue(T value, Queue<Tree<T>> queue) {
|
||||
Tree<T> 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 <T> Optional<Node<T>> search(T value, Node<T> start) {
|
||||
Queue<Node<T>> queue = new ArrayDeque<>();
|
||||
queue.add(start);
|
||||
|
||||
return searchNodeQueue(value, queue);
|
||||
}
|
||||
|
||||
private static <T> Optional<Node<T>> searchNodeQueue(T value, Queue<Node<T>> queue) {
|
||||
Node<T> currentNode;
|
||||
Set<Node<T>> 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.getNeighbours());
|
||||
queue.removeAll(alreadyVisited);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.algorithms.breadthfirstsearch;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Node<T> {
|
||||
|
||||
private T value;
|
||||
private Set<Node<T>> neighbours;
|
||||
|
||||
public Node(T value) {
|
||||
this.value = value;
|
||||
this.neighbours = new HashSet<>();
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Set<Node<T>> getNeighbours() {
|
||||
return Collections.unmodifiableSet(neighbours);
|
||||
}
|
||||
|
||||
public void connect(Node<T> node) {
|
||||
if (this == node) throw new IllegalArgumentException("Can't connect node to itself");
|
||||
this.neighbours.add(node);
|
||||
node.neighbours.add(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.algorithms.breadthfirstsearch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Tree<T> {
|
||||
|
||||
private T value;
|
||||
private List<Tree<T>> children;
|
||||
|
||||
private Tree(T value) {
|
||||
this.value = value;
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <T> Tree<T> of(T value) {
|
||||
return new Tree<>(value);
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public List<Tree<T>> getChildren() {
|
||||
return Collections.unmodifiableList(children);
|
||||
}
|
||||
|
||||
public Tree<T> addChild(T value) {
|
||||
Tree<T> newChild = new Tree<>(value);
|
||||
children.add(newChild);
|
||||
return newChild;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.baeldung.algorithms.breadthfirstsearch;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class BreadthFirstSearchAlgorithmUnitTest {
|
||||
|
||||
private Tree<Integer> root;
|
||||
private Tree<Integer> rootFirstChild;
|
||||
private Tree<Integer> depthMostChild;
|
||||
private Tree<Integer> rootSecondChild;
|
||||
|
||||
private Node<Integer> start;
|
||||
private Node<Integer> firstNeighbour;
|
||||
private Node<Integer> firstNeighbourNeighbour;
|
||||
private Node<Integer> secondNeighbour;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
initTree();
|
||||
initNode();
|
||||
}
|
||||
|
||||
private void initTree() {
|
||||
root = Tree.of(10);
|
||||
rootFirstChild = root.addChild(2);
|
||||
depthMostChild = rootFirstChild.addChild(3);
|
||||
rootSecondChild = root.addChild(4);
|
||||
}
|
||||
|
||||
private void initNode() {
|
||||
start = new Node<>(10);
|
||||
firstNeighbour = new Node<>(2);
|
||||
start.connect(firstNeighbour);
|
||||
|
||||
firstNeighbourNeighbour = new Node<>(3);
|
||||
firstNeighbour.connect(firstNeighbourNeighbour);
|
||||
firstNeighbourNeighbour.connect(start);
|
||||
|
||||
secondNeighbour = new Node<>(4);
|
||||
start.connect(secondNeighbour);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTree_whenSearchTen_thenRoot() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(10, root)).isPresent().contains(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTree_whenSearchThree_thenDepthMostValue() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(3, root)).isPresent().contains(depthMostChild);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTree_whenSearchFour_thenRootSecondChild() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(4, root)).isPresent().contains(rootSecondChild);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTree_whenSearchFive_thenNotFound() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(5, root)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNode_whenSearchTen_thenStart() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(10, firstNeighbourNeighbour)).isPresent().contains(start);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNode_whenSearchThree_thenNeighbourNeighbour() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(3, firstNeighbourNeighbour)).isPresent().contains(firstNeighbourNeighbour);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNode_whenSearchFour_thenSecondNeighbour() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(4, firstNeighbourNeighbour)).isPresent().contains(secondNeighbour);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenNode_whenSearchFive_thenNotFound() {
|
||||
assertThat(BreadthFirstSearchAlgorithm.search(5, firstNeighbourNeighbour)).isEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user