From f6e74736069431da9802b99a3260c40b88ac71d9 Mon Sep 17 00:00:00 2001 From: dupirefr Date: Mon, 2 Sep 2019 07:05:01 +0200 Subject: [PATCH 1/2] [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 --- .../BreadthFirstSearchAlgorithm.java | 61 +++++++++++++ .../algorithms/breadthfirstsearch/Node.java | 31 +++++++ .../algorithms/breadthfirstsearch/Tree.java | 34 ++++++++ .../BreadthFirstSearchAlgorithmUnitTest.java | 85 +++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java create mode 100644 algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Node.java create mode 100644 algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/Tree.java create mode 100644 algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java 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..4331b2fc94 --- /dev/null +++ b/algorithms-miscellaneous-3/src/main/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithm.java @@ -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 Optional> search(T value, Tree root) { + Queue> queue = new ArrayDeque<>(); + queue.add(root); + + return searchTreeQueue(value, queue); + } + + private static Optional> searchTreeQueue(T value, Queue> queue) { + 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); + + return searchNodeQueue(value, queue); + } + + private static Optional> searchNodeQueue(T value, Queue> queue) { + 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.getNeighbours()); + 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..bfa879f0b7 --- /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> neighbours; + + public Node(T value) { + this.value = value; + this.neighbours = new HashSet<>(); + } + + public T getValue() { + return value; + } + + public Set> getNeighbours() { + return Collections.unmodifiableSet(neighbours); + } + + public void connect(Node node) { + if (this == node) throw new IllegalArgumentException("Can't connect node to itself"); + this.neighbours.add(node); + node.neighbours.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..188cc32c3e --- /dev/null +++ b/algorithms-miscellaneous-3/src/test/java/com/baeldung/algorithms/breadthfirstsearch/BreadthFirstSearchAlgorithmUnitTest.java @@ -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 root; + private Tree rootFirstChild; + private Tree depthMostChild; + private Tree rootSecondChild; + + private Node start; + private Node firstNeighbour; + private Node firstNeighbourNeighbour; + private Node 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(); + } +} \ No newline at end of file From 223a767d0149e76c96ab5def32e461aac286daa3 Mon Sep 17 00:00:00 2001 From: dupirefr Date: Wed, 9 Oct 2019 05:15:45 +0200 Subject: [PATCH 2/2] [BAERL-3128] Fixes from editor review --- .../BreadthFirstSearchAlgorithm.java | 11 +-- .../algorithms/breadthfirstsearch/Node.java | 12 +-- .../BreadthFirstSearchAlgorithmUnitTest.java | 93 ++++++++++--------- 3 files changed, 55 insertions(+), 61 deletions(-) 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 index 4331b2fc94..9d301f9578 100644 --- 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 @@ -13,10 +13,6 @@ public class BreadthFirstSearchAlgorithm { Queue> queue = new ArrayDeque<>(); queue.add(root); - return searchTreeQueue(value, queue); - } - - private static Optional> searchTreeQueue(T value, Queue> queue) { Tree currentNode; while (!queue.isEmpty()) { currentNode = queue.remove(); @@ -36,10 +32,6 @@ public class BreadthFirstSearchAlgorithm { Queue> queue = new ArrayDeque<>(); queue.add(start); - return searchNodeQueue(value, queue); - } - - private static Optional> searchNodeQueue(T value, Queue> queue) { Node currentNode; Set> alreadyVisited = new HashSet<>(); @@ -51,11 +43,12 @@ public class BreadthFirstSearchAlgorithm { return Optional.of(currentNode); } else { alreadyVisited.add(currentNode); - queue.addAll(currentNode.getNeighbours()); + 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 index bfa879f0b7..54a589ae26 100644 --- 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 @@ -7,25 +7,25 @@ import java.util.Set; public class Node { private T value; - private Set> neighbours; + private Set> neighbors; public Node(T value) { this.value = value; - this.neighbours = new HashSet<>(); + this.neighbors = new HashSet<>(); } public T getValue() { return value; } - public Set> getNeighbours() { - return Collections.unmodifiableSet(neighbours); + 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.neighbours.add(node); - node.neighbours.add(this); + this.neighbors.add(node); + node.neighbors.add(this); } } 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 index 188cc32c3e..aa22fc5353 100644 --- 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 @@ -1,6 +1,5 @@ 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; @@ -13,14 +12,32 @@ class BreadthFirstSearchAlgorithmUnitTest { private Tree rootSecondChild; private Node start; - private Node firstNeighbour; - private Node firstNeighbourNeighbour; - private Node secondNeighbour; + private Node firstNeighbor; + private Node firstNeighborNeighbor; + private Node secondNeighbor; - @BeforeEach - void beforeEach() { + @Test + void givenTree_whenSearchTen_thenRoot() { initTree(); - initNode(); + 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() { @@ -30,56 +47,40 @@ class BreadthFirstSearchAlgorithmUnitTest { 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); + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(10, firstNeighborNeighbor)).isPresent().contains(start); } @Test - void givenNode_whenSearchThree_thenNeighbourNeighbour() { - assertThat(BreadthFirstSearchAlgorithm.search(3, firstNeighbourNeighbour)).isPresent().contains(firstNeighbourNeighbour); + void givenNode_whenSearchThree_thenNeighborNeighbor() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(3, firstNeighborNeighbor)).isPresent().contains(firstNeighborNeighbor); } @Test - void givenNode_whenSearchFour_thenSecondNeighbour() { - assertThat(BreadthFirstSearchAlgorithm.search(4, firstNeighbourNeighbour)).isPresent().contains(secondNeighbour); + void givenNode_whenSearchFour_thenSecondNeighbor() { + initNode(); + assertThat(BreadthFirstSearchAlgorithm.search(4, firstNeighborNeighbor)).isPresent().contains(secondNeighbor); } @Test void givenNode_whenSearchFive_thenNotFound() { - assertThat(BreadthFirstSearchAlgorithm.search(5, firstNeighbourNeighbour)).isEmpty(); + 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