public class MinPathTest { @Test public void nothing() throws Exception { } }
public void noGraph_NoPathZeroLength() throws Exception { Assert.assertEquals("{}0", minPath("")); }
private String minPath(String graph) { return "{}0"; }
minPath
call is incorrect. Dijkstra's algorithm allows you to find the shortest path between two given nodes. So, if we assume that the nodes are named, the test should actually look like this: Assert.assertEquals("{}0", minPath("", "A", "Z"));
private void assertPath(String graph, String expected) { assertEquals(expected, minPath(graph, "A", "Z")); } @Test public void noGraph_NoPathZeroLength() throws Exception { assertPath("", "{}0"); }
private void assertMinPath(String graph, int length, String path) { assertEquals(length, minLength(graph, "A", "Z")); assertEquals(path, minPath(graph, "A", "Z")); } @Test public void noGraph_NoPathZeroLength() throws Exception { assertMinPath("", 0, "{}"); } privateintminLength(String graph, String begin, String end) { return 0; } private String minPath(String graph, String begin, String end) { return "{}"; }
public class MinPathTest { private void assertMinPath(String graph, int length, String path) { PathFinder pf = new PathFinder(graph); assertEquals(length, pf.minLength("A", "Z")); assertEquals(path, pf.minPath("A", "Z")); } @Test public void noGraph_NoPathZeroLength() throws Exception { assertMinPath("", 0, "{}"); } } classPathFinder { publicPathFinder(String graph) { } publicintminLength(String begin, String end) { return 0; } public String minPath(String begin, String end) { return "{}"; } }
@Test public void degenerateCases() throws Exception { assertMinPath("", 0, "{}"); // assertMinPath("A", 0, "{}"); // assertMinPath("B1C", 0, "{}");// assertMinPath("A1C", 0, "{}");// assertMinPath("B1Z", 0, "{}");// }
@Test public void oneEdge() throws Exception { assertMinPath("A1Z", 1, "{AZ}"); }
private static String ANY = null; private void assertMinPath(String graph, Integer length, String path) { PathFinder pf = new PathFinder(graph); if (length != null) assertEquals((int)length, pf.minLength("A", "Z")); if (path != null) assertEquals(path, pf.minPath("A", "Z")); } ... @Test public void oneEdge() throws Exception { assertMinPath("A1Z", 1, ANY); }
public int minLength (String begin, String end) { if (graph.equals("A1Z")) return 1; return 0; }
String graph
into the PathFinder
constructor. This also means that the minPath
function minPath
not return the String
used by the combined statement. public class MinPathTest { private static String ANY = null; private void assertMinPath(String graph, Integer length, String path) { PathFinder pf = makePathFinder(graph); if (length != null) assertEquals((int) length, pf.minLength("A", "Z")); if (path != null) assertEquals(path, pf.minPath("A", "Z")); } privatePathFindermakePathFinder(String graph) { PathFinder pf = new PathFinder(); Pattern edgePattern = Pattern.compile("(\\D+)(\\d+)(\\D+)"); Matcher matcher = edgePattern.matcher(graph); if (matcher.matches()) { String start = matcher.group(1); int length = Integer.parseInt(matcher.group(2)); String end = matcher.group(3); pf.addEdge(start, end, length); } return pf; } @Test public void degenerateCases() throws Exception { assertMinPath("", 0, "{}"); // assertMinPath("A", 0, "{}"); // assertMinPath("B1C", 0, "{}");// assertMinPath("A1C", 0, "{}");// assertMinPath("B1Z", 0, "{}");// } @Test public void oneEdge() throws Exception { assertMinPath("A1Z", 1, ANY); } } classPathFinder { private List<Edge> edges = new ArrayList<>(); publicPathFinder() { } publicintminLength(String begin, String end) { int length = 0; for (Edge edge : edges) { if (edge.begin.equals(begin) &&edge.end.equals(end)) length += edge.length; } return length; } public String minPath(String begin, String end) { return "{}"; } public void addEdge(String start, String end, int length) { edges.add(new Edge(start, end, length)); } private static class Edge { public final String begin; public final String end; public final int length; public Edge(String begin, String end, int length) { this.begin = begin; this.end = end; this.length = length; } } }
ANY
. assertMinPath("A1Z", 1, "{AZ}");
toString
for lists. This test, as well as all subsequent ones will have to be changed as follows: @Test public void degenerateCases() throws Exception { assertMinPath("", 0, "[]"); // assertMinPath("A", 0, "[]"); // assertMinPath("B1C", 0, "[]"); // assertMinPath("A1C", 0, "[]"); // assertMinPath("B1Z", 0, "[]"); // } @Test public void oneEdge() throws Exception { assertMinPath("A1Z", 1, "[A, Z]"); }
assertMinPath
, by adding the toString
call. if (path != null) assertEquals(path, pf.minPath("A", "Z").toString()); ...
path
list to the PathFinder
and simply load it into the minLength
function. classPathFinder { private List<Edge> edges = new ArrayList<>(); ... publicintminLength(String begin, String end) { int length = 0; for (Edge edge : edges) { if (edge.begin.equals(begin) &&edge.end.equals(end)) { length += edge.length; path.add(edge.begin); path.add(edge.end); } } return length; } public List<String>minPath(String begin, String end) { return path; } ...
private void assertMinPath(String graph, Integer length, String path) { PathFinder pf = makePathFinder(graph); if (length != null) assertEquals((int) length, pf.getLength()); if (path != null) assertEquals(path, pf.getPath().toString()); } privatePathFindermakePathFinder(String graph) { PathFinder pf = new PathFinder(); ... pf.findPath("A", "Z"); return pf; } classPathFinder { private List<Edge> edges = new ArrayList<>(); private List<String> path = new ArrayList<>(); privateint length; ... publicintgetLength() { return length; } public List<String>getPath() { return path; } ...
assertMinPath("A2Z", 2, "[A, Z]");
@Test public void twoEdges() throws Exception { assertMinPath("A1B,B1Z", 2, ANY); }
privatePathFinder makePathFinder(String graph) { PathFinder pf = new PathFinder(); Pattern edgePattern = Pattern.compile("(\\D+)(\\d+)(\\D+)"); String[] edges = graph.split(","); for (String edge : edges) { Matcher matcher = edgePattern.matcher(edge); if (matcher.matches()) { String start = matcher.group(1); int length = Integer.parseInt(matcher.group(2)); String end = matcher.group(3); pf.addEdge(start, end, length); } } pf.findPath("A", "Z"); return pf; }
&&
with ||
in the findPath
function: public void findPath(String begin, String end) { length = 0; for (Edge edge : edges) { if (edge.begin.equals(begin) || edge.end.equals(end)) { length += edge.length; path.add(edge.begin); path.add(edge.end); } } }
&&
to ||
. Yes, stupid. This solution will work only with two consecutive edges. We are already building Napoleonic plans. But, after all, it is not suitable.twoEdges
test, and with the oneEdge
tests, but we fail the degenerateCases
tests. public void findPath(String begin, String end) { if (edges.size() == 0) return; else if (edges.size() == 1) { Edge edge = edges.get(0); if (edge.begin.equals(begin) &&edge.end.equals(end)) { path.add(edge.begin); path.add(edge.end); length += edge.length; } } else { for (Edge edge : edges) { if (edge.begin.equals(begin) || edge.end.equals(end)) { path.add(edge.begin); path.add(edge.end); length += edge.length; } } } }
assertMinPath ("A1B, B1Z", 2, "[A, B, Z]");
fails because it gives [A, B, B, Z]
. This could be fixed by adding another horrible if
; but there is a better idea. Let's go through the graph again from beginning to end. public void findPath(String begin, String end) { List<String> p = new ArrayList<>(); int l = 0; p.add(begin); for (Edge e = findEdge(begin); e != null; e = findEdge(e.end)) { p.add(e.end); l += e.length; if (e.end.equals(end)) { length = l; path = p; return; } } } private Edge findEdge(String begin) { for (Edge e : edges) { if (e.begin.equals(begin)) return e; } return null; }
@Test public void twoEdges() throws Exception { assertMinPath ("A1B,B1Z", 2, "[A, B, Z]"); assertMinPath ("B1Z,A1B", 2, "[A, B, Z]"); assertMinPath ("A1X,Y1Z", 0, "[]"); }
@Test public void threeEdges() throws Exception { assertMinPath ("A2B,B3C,C4Z", 9, "[A, B, C, Z]"); assertMinPath ("B3C,C4Z,A2B", 9, "[A, B, C, Z]"); } @Test public void OnlyOnePath() throws Exception { assertMinPath ("A1B,B2C,C3Z,B4D,D6E", 6, "[A, B, C, Z]"); }
assertMinPath ("A1B,B2C,C3D,C3Z", 6, "[A, B, C, Z]");
Integer.MAX_VALUE
as a signal value: "unreachable from the visited node". The search is limited to only those nodes that have been achieved, since we are still going from beginning to end. Any node that could not be reached, obviously, is not the next on the way. classPathFinder { private List<Edge> edges = new ArrayList<>(); private Set<String>nodeNames = new HashSet<>(); private Map<String, Node> nodes = new HashMap<>(); private Node endNode; public void findPath(String begin, String end) { List<String> unvisited = initializeSearch(begin, end); for (String node = begin; node != null; node = getNext(unvisited)) { unvisited.remove(node); visit(node); } setupEndNode(end); } private List<String>initializeSearch(String begin, String end) { nodeNames.add(begin); nodeNames.add(end); List<String> unvisited = new ArrayList<>(nodeNames); for (String node : unvisited) nodes.put(node, new Node(Integer.MAX_VALUE)); nodes.get(begin).length = 0; return unvisited; } private void visit(String node) { List<Edge> neighbors = findEdges(node); Node curNode = nodes.get(node); for (Edge e : neighbors) { Node nbr = nodes.get(e.end); nbr.length = curNode.length + e.length; nbr.path = new ArrayList<String>(); nbr.path.addAll(curNode.path); nbr.path.add(node); } } private void setupEndNode(String end) { endNode = nodes.get(end); if (endNode.length != Integer.MAX_VALUE) endNode.path.add(end); else endNode.length = 0; } private String getNext(List<String> unvisited) { for (String name : unvisited) { Node candidate = nodes.get(name); if (candidate.length != Integer.MAX_VALUE) return name; } return null; } private List<Edge>findEdges(String begin) { List<Edge> found = new ArrayList<>(); for (Edge e : edges) { if (e.begin.equals(begin)) found.add(e); } return found; } publicintgetLength() { returnendNode.length; } public List<String>getPath() { returnendNode.path; } public void addEdge(String start, String end, int length) { edges.add(new Edge(start, end, length)); nodeNames.add(start); nodeNames.add(end); } private static class Edge { public final String begin; public final String end; public final int length; public Edge(String begin, String end, int length) { this.begin = begin; this.end = end; this.length = length; } } private static class Node { publicint length; public List<String> path; public Node(int l) { this.length = l; this.path = new ArrayList<>(); } } }
assertMinPath ("A1B,B2Z,A1Z", 1, "[A, Z]");
Integer.MAX_VALUE
, then this node is reached via a different path. Since we are interested in the minimum, you can simply set the length value on this node as the minimum for converging paths. It turns out that the Integer.MAX_VALUE
value Integer.MAX_VALUE
very convenient as a signal label, since it replaces the “infinite” length. private void visit(String node) { List<Edge> neighbors = findEdges(node); Node curNode = nodes.get(node); for (Edge e : neighbors) { Node nbr = nodes.get(e.end); intnewLength = curNode.length + e.length; if (nbr.length>newLength) { nbr.length = newLength; nbr.path = new ArrayList<String>(); nbr.path.addAll(curNode.path); nbr.path.add(node); } } }
for (String node = begin; node != null && !node.equals(end); node = getNext(unvisited)) { unvisited.remove(node); visit(node); }
private String getNext(List<String> unvisited) { String minNodeName = null; intminLength = Integer.MAX_VALUE; for (String name : unvisited) { Node candidate = nodes.get(name); if (candidate.length<minLength) { minLength = candidate.length; minNodeName = name; } } return minNodeName; }
package dijkstrasAlg; import org.junit.Test; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.junit.Assert.assertEquals; public class MinPathTest { private static String ANY = null; private void assertMinPath(String graph, Integer length, String path) { PathFinder pf = makePathFinder(graph); if (length != null) assertEquals((int) length, pf.getLength()); if (path != null) assertEquals(path, pf.getPath().toString()); } private PathFinder makePathFinder(String graph) { PathFinder pf = new PathFinder(); Pattern edgePattern = Pattern.compile("(\\D+)(\\d+)(\\D+)"); String[] edges = graph.split(","); for (String edge : edges) { Matcher matcher = edgePattern.matcher(edge); if (matcher.matches()) { String start = matcher.group(1); int length = Integer.parseInt(matcher.group(2)); String end = matcher.group(3); pf.addEdge(start, end, length); } } pf.findPath("A", "Z"); return pf; } @Test public void degenerateCases() throws Exception { assertMinPath("", 0, "[]"); // assertMinPath("A", 0, "[]"); // assertMinPath("B1C", 0, "[]");// assertMinPath("A1C", 0, "[]");// assertMinPath("B1Z", 0, "[]");// } @Test public void oneEdge() throws Exception { assertMinPath("A1Z", 1, "[A, Z]"); assertMinPath("A2Z", 2, "[A, Z]"); } @Test public void twoEdges() throws Exception { assertMinPath("A1B,B1Z", 2, "[A, B, Z]"); assertMinPath("B1Z,A1B", 2, "[A, B, Z]"); assertMinPath("A1X,Y1Z", 0, "[]"); } @Test public void threeEdges() throws Exception { assertMinPath("A2B,B3C,C4Z", 9, "[A, B, C, Z]"); assertMinPath("B3C,C4Z,A2B", 9, "[A, B, C, Z]"); } @Test public void OnlyOnePath() throws Exception { assertMinPath("A1B,B2C,C3Z,B4D,D6E", 6, "[A, B, C, Z]"); assertMinPath("A1B,B2C,C3D,C3Z", 6, "[A, B, C, Z]"); } @Test public void parallelPaths() throws Exception { assertMinPath("A1B,B2Z,A1Z", 1, "[A, Z]"); assertMinPath("A1B,A1C,A2D,C5E,B8E,C1F,D3F,F2G,G3Z,E2G", 7,"[A, C, F, G, Z]"); } } class PathFinder { private List<Edge> edges = new ArrayList<>(); private Set<String> nodeNames = new HashSet<>(); private Map<String, Node> nodes = new HashMap<>(); private Node endNode; public void findPath(String begin, String end) { List<String> unvisited = initializeSearch(begin, end); for (String node = begin; node != null && !node.equals(end); node = getNext(unvisited)) { unvisited.remove(node); visit(node); } setupEndNode(end); } private List<String> initializeSearch(String begin, String end) { nodeNames.add(begin); nodeNames.add(end); List<String> unvisited = new ArrayList<>(nodeNames); for (String node : unvisited) nodes.put(node, new Node(Integer.MAX_VALUE)); nodes.get(begin).length = 0; return unvisited; } private void visit(String node) { List<Edge> neighbors = findEdges(node); Node curNode = nodes.get(node); for (Edge e : neighbors) { Node nbr = nodes.get(e.end); int newLength = curNode.length + e.length; if (nbr.length > newLength) { nbr.length = newLength; nbr.path = new ArrayList<String>(); nbr.path.addAll(curNode.path); nbr.path.add(node); } } } private void setupEndNode(String end) { endNode = nodes.get(end); if (endNode.length != Integer.MAX_VALUE) endNode.path.add(end); else endNode.length = 0; } private String getNext(List<String> unvisited) { String minNodeName = null; int minLength = Integer.MAX_VALUE; for (String name : unvisited) { Node candidate = nodes.get(name); if (candidate.length < minLength) { minLength = candidate.length; minNodeName = name; } } return minNodeName; } private List<Edge> findEdges(String begin) { List<Edge> found = new ArrayList<>(); for (Edge e : edges) { if (e.begin.equals(begin)) found.add(e); } return found; } public int getLength() { return endNode.length; } public List<String> getPath() { return endNode.path; } public void addEdge(String start, String end, int length) { edges.add(new Edge(start, end, length)); nodeNames.add(start); nodeNames.add(end); } private static class Edge { public final String begin; public final String end; public final int length; public Edge(String begin, String end, int length) { this.begin = begin; this.end = end; this.length = length; } } private static class Node { public int length; public List<String> path; public Node(int l) { this.length = l; this.path = new ArrayList<>(); } } }
Source: https://habr.com/ru/post/319490/
All Articles