[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:
dupirefr
2019-09-02 07:05:01 +02:00
parent 5cffe22228
commit f6e7473606
4 changed files with 211 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}