Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions src/main/java/algorithm/BreadthFirstSearch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package algorithm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import static java.util.function.Predicate.not;
import static java.util.stream.Stream.iterate;

public class BreadthFirstSearch<T> implements SearchAlgorithm<T> {
@Override
public List<T> findPath(Graph<T> graph, T source, T target) {
final var queue = new LinkedList<T>();
final var visited = new HashSet<T>();
final var previous = new HashMap<T, T>();
queue.add(source);

while (!queue.isEmpty()) {
final var node = queue.removeFirst();
if (target.equals(node)) {
final var path = new LinkedList<T>();
iterate(node, Objects::nonNull, previous::get).forEach(path::addFirst);
return path;
}
visited.add(node);
graph.nodes().get(node).keySet().stream()
.filter(not(visited::contains))
.forEach(it -> {
previous.computeIfAbsent(it, x -> node);
queue.addLast(it);
});
}
return new LinkedList<>();
}

}
15 changes: 10 additions & 5 deletions src/main/java/algorithm/Dijkstras.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package algorithm;

import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

public class Dijkstras<T> implements Algorithm<T> {
public class Dijkstras<T> implements SearchAlgorithm<T> {

@Override
public List<T> findPath(Graph<T> graph, T source, T target) {
Expand All @@ -12,14 +16,15 @@ public List<T> findPath(Graph<T> graph, T source, T target) {
final var distances = new HashMap<T, Double>();
final var previous = new HashMap<T, T>();
queue.add(source);
distances.put(source, .0);

while (!queue.isEmpty()) {
final var prev = queue.pollFirst();
final var prev = queue.removeFirst();
final var edges = graph.nodes().get(prev);
edges.forEach((node, time) -> {
final var distance = distances.getOrDefault(prev, .0) + time.doubleValue();
final var distance = distances.get(prev) + time.doubleValue();
if (!visited.contains(node)) {
queue.add(node);
queue.addLast(node);
visited.add(node);
}
if (distance < distances.getOrDefault(node, Double.MAX_VALUE)) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/algorithm/Graph.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

public record Graph<T>(Map<T, Map<T, Number>> nodes) {

public double getDistance(List<T> path) {
public double getDistance(final List<T> path) {
double distance = 0;
for (int i = 1; i < path.size(); ++i) {
final var previous = nodes.get(path.get(i - 1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

public interface Algorithm<T> {
@FunctionalInterface
public interface SearchAlgorithm<T> {
List<T> findPath(Graph<T> graph, T source, T target);
}
71 changes: 71 additions & 0 deletions src/test/groovy/algorithm/BreadthFirstSearchSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package algorithm

import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Unroll

class BreadthFirstSearchSpec extends Specification {
@Subject
def algorithm = new BreadthFirstSearch()

@Unroll("from #source to #target the time is #time and the path is #shortest")
def 'should find a route for simple graph'() {
given:
def graph = new Graph([
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
])

when:
def path = algorithm.findPath(graph, source, target)

then:
path == shortest

and:
graph.getDistance(path) == time as double

where:
source | target || time | shortest
'A' | 'A' || 0 | ['A']
'A' | 'B' || 7 | ['A', 'B']
'B' | 'C' || 5 | ['B', 'C']
'C' | 'B' || 3 | ['C', 'B']
}

@Unroll("from #source to #target the time is #time and the path is #shortest")
def 'should find a route for complex graph'() {
given:
def graph = new Graph([
A: [B: 1],
B: [A: 1, D: 1],
C: [A: 1],
D: [C: 1, E: 1],
E: [F: 1],
F: [D: 1, E: 1]])

when:
def path = algorithm.findPath(graph, source, target)

then:
path == shortest

and:
graph.getDistance(path) == time as double

where:
source | target || shortest
'A' | 'A' || ['A']
'B' | 'B' || ['B']
'A' | 'B' || ['A', 'B']
'B' | 'A' || ['B', 'A']
'A' | 'C' || ['A', 'B', 'D', 'C']
'C' | 'A' || ['C', 'A']
'E' | 'B' || ['E', 'F', 'D', 'C', 'A', 'B']

and:
time = shortest.size() - 1
}

}
69 changes: 56 additions & 13 deletions src/test/groovy/algorithm/DijkstrasSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,72 @@ import spock.lang.Subject
import spock.lang.Unroll

class DijkstrasSpec extends Specification {

static final SAMPLE_ONE = [
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
] as Graph<String>

@Subject
def algorithm = new Dijkstras<String>()

@Unroll("from #source to #target the time is #time and the path is #expected")
def 'should find a route for sample one'() {
@Unroll("from #source to #target the time is #time and the path is #fastest")
def 'should find a route for a simple graph'() {
given:
def graph = SAMPLE_ONE
def graph = new Graph([
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
])

when:
def path = algorithm.findPath(graph, source, target)

then:
path == expected
path == fastest

and:
graph.getDistance(path) == time as double

where:
source | target || time | expected
'A' | 'B' || 7 | ['A', 'B']
source | target || time | fastest
'A' | 'A' || 0 | ['A']
'B' | 'B' || 0 | ['B']
'C' | 'C' || 0 | ['C']
'A' | 'B' || 5 | ['A', 'C', 'B']
}

@Unroll("from #source to #target the time is #time and the path is #fastest")
def 'should find a route for a complex graph'() {
given:
def graph = new Graph([
A: [B: 5, H: 2],
B: [A: 5, C: 7],
C: [B: 7, D: 3, G: 4],
D: [C: 20, E: 4],
E: [F: 5],
F: [G: 6],
G: [C: 4],
H: [G: 3]
])

when:
def path = algorithm.findPath(graph, source, target)

then:
path == fastest

and:
graph.getDistance(path) == time as double

where:
source | target || time | fastest
'A' | 'A' || 0 | ['A']
'B' | 'B' || 0 | ['B']
'A' | 'B' || 5 | ['A', 'B']
'B' | 'A' || 5 | ['B', 'A']
'A' | 'C' || 9 | ['A', 'H', 'G', 'C']
'C' | 'A' || 12 | ['C', 'B', 'A']
'A' | 'G' || 5 | ['A', 'H', 'G']
'C' | 'D' || 3 | ['C', 'D']
'D' | 'C' || 19 | ['D', 'E', 'F', 'G', 'C']
'B' | 'D' || 10 | ['B', 'C', 'D']
'D' | 'B' || 26 | ['D', 'E', 'F', 'G', 'C', 'B']
'D' | 'H' || 33 | ['D', 'E', 'F', 'G', 'C', 'B', 'A', 'H']
}

}