I am trying to use the following code for Dijktras Algorithm to implement the OSPF routing protocol in java. I want to make this algorithm to except the values for the Edges and their cost dynamically.
import java.io.*;
import java.util.*;
public class Dijkstra {
private static final Graph.Edge[] GRAPH = {
new Graph.Edge("a", "b", 7),
new Graph.Edge("a", "c", 9),
new Graph.Edge("a", "f", 14),
new Graph.Edge("b", "c", 10),
new Graph.Edge("b", "d", 15),
new Graph.Edge("c", "d", 11),
new Graph.Edge("c", "f", 2),
new Graph.Edge("d", "e", 6),
new Graph.Edge("e", "f", 9),
};
private static final String START = "a";
private static final String END = "e";
public static void main(String[] args) {
Graph g = new Graph(GRAPH);
g.dijkstra(START);
g.printPath(END);
//g.printAllPaths();
}
}
class Graph {
private final Map<String, Vertex> graph; // mapping of vertex names to Vertex objects, built from a set of Edges
/** One edge of the graph (only used by Graph constructor) */
public static class Edge {
public final String v1, v2;
public final int dist;
public Edge(String v1, String v2, int dist) {
this.v1 = v1;
this.v2 = v2;
this.dist = dist;
}
}
/** One vertex of the graph, complete with mappings to neighbouring vertices */
public static class Vertex implements Comparable<Vertex> {
public final String name;
public int dist = Integer.MAX_VALUE; // MAX_VALUE assumed to be infinity
public Vertex previous = null;
public final Map<Vertex, Integer> neighbours = new HashMap<>();
public Vertex(String name) {
this.name = name;
}
private void printPath() {
if (this == this.previous) {
System.out.printf("%s", this.name);
} else if (this.previous == null) {
System.out.printf("%s(unreached)", this.name);
} else {
this.previous.printPath();
System.out.printf(" -> %s(%d)", this.name, this.dist);
}
}
public int compareTo(Vertex other) {
return Integer.compare(dist, other.dist);
}
}
/** Builds a graph from a set of edges */
public Graph(Edge[] edges) {
graph = new HashMap<>(edges.length);
//one pass to find all vertices
for (Edge e : edges) {
if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex(e.v1));
if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex(e.v2));
}
//another pass to set neighbouring vertices
for (Edge e : edges) {
graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
//graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph
}
}
/** Runs dijkstra using a specified source vertex */
public void dijkstra(String startName) {
if (!graph.containsKey(startName)) {
System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName);
return;
}
final Vertex source = graph.get(startName);
NavigableSet<Vertex> q = new TreeSet<>();
// set-up vertices
for (Vertex v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Integer.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
/** Implementation of dijkstra's algorithm using a binary heap. */
private void dijkstra(final NavigableSet<Vertex> q) {
Vertex u, v;
while (!q.isEmpty()) {
u = q.pollFirst(); // vertex with shortest distance (first iteration will return source)
if (u.dist == Integer.MAX_VALUE) break; // we can ignore u (and any other remaining vertices) since they are unreachable
//look at distances to each neighbour
for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) {
v = a.getKey(); //the neighbour in this iteration
final int alternateDist = u.dist + a.getValue();
if (alternateDist < v.dist) { // shorter path to neighbour found
q.remove(v);
v.dist = alternateDist;
v.previous = u;
q.add(v);
}
}
}
}
/** Prints a path from the source to the specified vertex */
public void printPath(String endName) {
if (!graph.containsKey(endName)) {
System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName);
return;
}
graph.get(endName).printPath();
System.out.println();
}
/** Prints the path from the source to every vertex (output order is not guaranteed) */
public void printAllPaths() {
for (Vertex v : graph.values()) {
v.printPath();
System.out.println();
}
}
}
I tried following
public static void main(String[] args) {
Map<String, ArrayList<String>> m = new HashMap<String, ArrayList<String>>();
ArrayList<String> l1 = new ArrayList<String>();
l1.add("R1");
l1.add("R2");
l1.add("50");
ArrayList<String> l2 = new ArrayList<String>();
l2.add("R1");
l2.add("R3");
l2.add("50");
ArrayList<String> l3 = new ArrayList<String>();
l3.add("R4");
l3.add("R3");
l3.add("50");
ArrayList<String> l4 = new ArrayList<String>();
l4.add("R4");
l4.add("R2");
l4.add("50");
m.put("X", l1);
m.put("Y", l2);
m.put("Z", l3);
m.put("W", l4);
System.out.println(m);
Graph.Edge[] GRAPH=null;
// private static final Graph.Edge[] GRAPH = null;
int i=0;
for(String key:m.keySet()){
System.out.println(key+m.get(key).get(0)+m.get(key).get(1)+m.get(key).get(2));
GRAPH[i]=new Graph.Edge(m.get(key).get(0), m.get(key).get(1),Integer.parseInt(m.get(key).get(2)));
i++;
}
String START = "a";
String END = "e";
Graph g = new Graph(GRAPH);
g.dijkstra(START);
g.printPath(END);
//g.printAllPaths();
}
}
I want to pass the value from the hashmap to the dijktras algorithm. and initialize all the edges with the data stored in the HashMap
Note: hashmap<LinkID,<Edge,Edge,Cost>>
Please Suggest where I am going wrong or suggest a better of passing the values dynamically to this Algorithm
The example you link uses a hard-coded array
private static final Graph.Edge[] GRAPH = {
new Graph.Edge("a", "b", 7),
new Graph.Edge("a", "c", 9),
new Graph.Edge("a", "f", 14),
new Graph.Edge("b", "c", 10),
new Graph.Edge("b", "d", 15),
new Graph.Edge("c", "d", 11),
new Graph.Edge("c", "f", 2),
new Graph.Edge("d", "e", 6),
new Graph.Edge("e", "f", 9),
};
for initialization of the graph. First of all, it is a bit surprising that you use seem to use strings for encoding of the distances. For conversion, you need to iterate over the keys of your Map (which would enumerate the edges) and create an instance of Graph.Edege for each, which would then populated with both the vertex identifiers and the distance. Iteration over keys of a Map can be found in this example.
Related
I using this exact code for this. I modified it a little. So far I added a start and end node index to the calculateShortestDistances() method. Also the path ArrayList for collecting the path node indexes. Also: new to Java...
How do I collect the indexes of nodes in the path ArrayList?
I just can't come up with the solution on a level that I am not even positive this code could do what I want. I only have intuition on my side and little time.
What I tried:
Adding the nextNode value to the list then removing it if it was not
a shorter distance.
Adding the neighbourIndex to the list then removing it if it was not a shorter distance.
I made a Path.java with ArrayList but that was went nowhere (it was a class with a public variable named path) but it went nowhere.
Main.java:
public class Main {
public static void main(String[] args) {
Edge[] edges = {
new Edge(0, 2, 1), new Edge(0, 3, 4), new Edge(0, 4, 2),
new Edge(0, 1, 3), new Edge(1, 3, 2), new Edge(1, 4, 3),
new Edge(1, 5, 1), new Edge(2, 4, 1), new Edge(3, 5, 4),
new Edge(4, 5, 2), new Edge(4, 6, 7), new Edge(4, 7, 2),
new Edge(5, 6, 4), new Edge(6, 7, 5)
};
Graph g = new Graph(edges);
g.calculateShortestDistances(4,6);
g.printResult(); // let's try it !
System.out.println(g.path);
}
}
Graph.java:
This is the Graph.java file. Here I added a sAt and eAt variable, so I can tell it what path I am after. Also I created a public path ArrayList, where I intend to collect the path.
import java.util.ArrayList;
// now we must create graph object and implement dijkstra algorithm
public class Graph {
private Node[] nodes;
private int noOfNodes;
private Edge[] edges;
private int noOfEdges;
private int sAt;
private int eAt;
public ArrayList<Integer> path = new ArrayList<>();
public Graph(Edge[] edges) {
this.edges = edges;
// create all nodes ready to be updated with the edges
this.noOfNodes = calculateNoOfNodes(edges);
this.nodes = new Node[this.noOfNodes];
for (int n = 0; n < this.noOfNodes; n++) {
this.nodes[n] = new Node();
}
// add all the edges to the nodes, each edge added to two nodes (to and from)
this.noOfEdges = edges.length;
for (int edgeToAdd = 0; edgeToAdd < this.noOfEdges; edgeToAdd++) {
this.nodes[edges[edgeToAdd].getFromNodeIndex()].getEdges().add(edges[edgeToAdd]);
this.nodes[edges[edgeToAdd].getToNodeIndex()].getEdges().add(edges[edgeToAdd]);
}
}
private int calculateNoOfNodes(Edge[] edges) {
int noOfNodes = 0;
for (Edge e : edges) {
if (e.getToNodeIndex() > noOfNodes)
noOfNodes = e.getToNodeIndex();
if (e.getFromNodeIndex() > noOfNodes)
noOfNodes = e.getFromNodeIndex();
}
noOfNodes++;
return noOfNodes;
}
public void calculateShortestDistances(int startAt, int endAt) {
// node 0 as source
this.sAt = startAt;
this.eAt = endAt;
this.nodes[startAt].setDistanceFromSource(0);
int nextNode = startAt;
// visit every node
for (int i = 0; i < this.nodes.length; i++) {
// loop around the edges of current node
ArrayList<Edge> currentNodeEdges = this.nodes[nextNode].getEdges();
for (int joinedEdge = 0; joinedEdge < currentNodeEdges.size(); joinedEdge++) {
int neighbourIndex = currentNodeEdges.get(joinedEdge).getNeighbourIndex(nextNode);
// only if not visited
if (!this.nodes[neighbourIndex].isVisited()) {
int tentative = this.nodes[nextNode].getDistanceFromSource() + currentNodeEdges.get(joinedEdge).getLength();
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
}
}
}
// all neighbours checked so node visited
nodes[nextNode].setVisited(true);
// next node must be with shortest distance
nextNode = getNodeShortestDistanced();
}
}
// now we're going to implement this method in next part !
private int getNodeShortestDistanced() {
int storedNodeIndex = 0;
int storedDist = Integer.MAX_VALUE;
for (int i = 0; i < this.nodes.length; i++) {
int currentDist = this.nodes[i].getDistanceFromSource();
if (!this.nodes[i].isVisited() && currentDist < storedDist) {
storedDist = currentDist;
storedNodeIndex = i;
}
}
return storedNodeIndex;
}
// display result
public void printResult() {
String output = "Number of nodes = " + this.noOfNodes;
output += "\nNumber of edges = " + this.noOfEdges;
output += "\nDistance from "+sAt+" to "+eAt+":" + nodes[eAt].getDistanceFromSource();
System.out.println(output);
}
public Node[] getNodes() {
return nodes;
}
public int getNoOfNodes() {
return noOfNodes;
}
public Edge[] getEdges() {
return edges;
}
public int getNoOfEdges() {
return noOfEdges;
}
}
Addittionally here are the Edge.java and the Node.java classes.
Node.java:
import java.util.ArrayList;
public class Node {
private int distanceFromSource = Integer.MAX_VALUE;
private boolean visited;
private ArrayList<Edge> edges = new ArrayList<Edge>(); // now we must create edges
public int getDistanceFromSource() {
return distanceFromSource;
}
public void setDistanceFromSource(int distanceFromSource) {
this.distanceFromSource = distanceFromSource;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public ArrayList<Edge> getEdges() {
return edges;
}
public void setEdges(ArrayList<Edge> edges) {
this.edges = edges;
}
}
Edge.java
public class Edge {
private int fromNodeIndex;
private int toNodeIndex;
private int length;
public Edge(int fromNodeIndex, int toNodeIndex, int length) {
this.fromNodeIndex = fromNodeIndex;
this.toNodeIndex = toNodeIndex;
this.length = length;
}
public int getFromNodeIndex() {
return fromNodeIndex;
}
public int getToNodeIndex() {
return toNodeIndex;
}
public int getLength() {
return length;
}
// determines the neighbouring node of a supplied node, based on the two nodes connected by this edge
public int getNeighbourIndex(int nodeIndex) {
if (this.fromNodeIndex == nodeIndex) {
return this.toNodeIndex;
} else {
return this.fromNodeIndex;
}
}
}
I know it looks like a homework. Trust me it isn't. On the other hand I have not much time to finish it, that is why I do it at Sunday. Also I am aware how Dijkstra algorithm works, I understand the concept, I can do it on paper. But collecting the path is beyond me.
Thanks for Christian H. Kuhn's and second's comments I managed to come up with the code.
I modified it as follows (I only put in the relevant parts)
Node.java
Here I added a setPredecessor(Integer predecessor) and a getPredecessor() methods to set and get the value of the private variable predecessor (so I follow the original code's style too).
[...]
private int predecessor;
[...]
public int getPredecessor(){
return predecessor;
}
public void setPredecessor(int predecessor){
this.predecessor = predecessor;
}
[...]
Graph.java
Here I created the calculatePath() and getPath() methods. calculatePath() does what the commenters told me to do. The getPath() returns the ArrayLists for others to use.
[...]
private int sAt;
private int eAt;
private ArrayList<Integer> path = new ArrayList<Integer>();
[...]
public void calculateShortestDistances(int startAt, int endAt) {
[...]
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
nodes[neighbourIndex].setPredecessor(nextNode);
}
[...]
public void calculatePath(){
int nodeNow = eAt;
while(nodeNow != sAt){
path.add(nodes[nodeNow].getPredecessor());
nodeNow = nodes[nodeNow].getPredecessor();
}
}
public ArrayList<Integer> getPath(){
return path;
}
[...]
Main.java so here I can do this now:
[...]
Graph g = new Graph(edges);
g.calculateShortestDistances(5,8);
g.calculatePath();
String results = "";
ArrayList<Integer> path = g.getPath();
System.out.println(path);
[...]
I know it shows the path backwards, but that is not a problem, as I can always reverse it. The point is: I not only have the the distance from node to node, but the path through nodes too. Thank you for the help.
Starting with a set of sets "groups":
Set<Set<String>> groups = new HashSet<>();
I want to create a new list of sets by merging all subsets with common elements:
i.e. Starting with the sets below:
A = {a, b, c}
B = {c, d, e, f}
C = {f, g, h, i, j}
D = {k, l, m}
E = {m, n, o}
F = {p, q, r}
The final result would be:
Set 1 = {a, b, c, d, e, f, g, h, i, j}
Set 2 = {k, l, m, n, o}
Set 3 = {p, q, r}
Any advice on how to accomplish this would be appreciated.
EDIT: In case of uneven sets it would perform the same. So if it were a method, it pseudo would look like this:
public void doStuff(){
Set<Set<String>> groups = {{a,b,c}, {c,d,e,f}, {m, n, o}}
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups){
//some operations
}
Console out:
New Groups: {{a,b,c,d,e,f}, {m, n, o}}
You can just implement the algorithm as you describe it in your problem statement -- find intersecting sets and merge them until there is nothing to merge. Standard library has a method Collections.disjoint that helps by determining if two collections have any elements in common:
// this implementation sacrifices efficiency for clarity
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups) {
Set<Set<String>> result = new HashSet<>();
for (Set<String> set : groups) {
// try to find a set in result that intersects this set
// if one is found, merge the two. otherwise, add this set to result
result.stream()
.filter(x -> !Collections.disjoint(x, set))
.findAny()
.ifPresentOrElse( // this method was added in java 9
x -> x.addAll(set),
() -> result.add(new HashSet<>(set))
);
}
// if nothing got merged we are done; otherwise, recurse and try again
return result.size() == groups.size() ? result : mergeSubsets(result);
}
Here is the imperative way based on #NiksVij solution. Obviously the solution of #NiksVij is not correct and this answer aims to fix this and extend a bit more:
public class MergeSet {
public static void main(String... args) {
List<Set<String>> list = new ArrayList<>();
String[] A = {"a", "c", "e", "g"};
String[] B = {"b", "d", "f", "h"};
String[] C = {"c", "e", "f"};
String[] D = {"b"};
list.add(new HashSet<>(Arrays.asList(A)));
list.add(new HashSet<>(Arrays.asList(C)));
list.add(new HashSet<>(Arrays.asList(B)));
list.add(new HashSet<>(Arrays.asList(D)));
List<Set<String>> newGroups = merge(list);
System.out.println(newGroups);
}
#SuppressWarnings("empty-statement")
private static <T> List<Set<T>> merge(List<Set<T>> list) {
if (list == null || list.isEmpty()) {
return list;
}
List<Set<T>> merged = new ArrayList<>();
do {
merged.add(list.get(0));
list.remove(0);
while (mergeStep(merged.get(merged.size() - 1), list));
} while (!list.isEmpty());
return merged;
}
private static <T> boolean mergeStep(Set<T> setToCheck, List<Set<T>> remainingList) {
boolean atLeastOnceMerged = false;
Iterator<Set<T>> iterator = remainingList.iterator();
while (iterator.hasNext()) {
Set<T> elements = iterator.next();
boolean doMerge = !Collections.disjoint(elements, setToCheck);
if (doMerge) {
atLeastOnceMerged |= doMerge;
setToCheck.addAll(elements);
iterator.remove();
}
}
return atLeastOnceMerged;
}
import java.util.*;
public class MergeSet {
public static void main(String... args) {
List<Set<String>> groups = new ArrayList<>();
String[] A = {"a", "b", "c"};
String[] B = {"c", "d", "e", "f"};
String[] C = {"f", "g", "h", "i", "j"};
String[] D = {"k", "l", "m"};
String[] E = {"m", "n", "o"};
String[] F = {"p", "q", "r"};
groups.add(new HashSet<>(Arrays.asList(A)));
groups.add(new HashSet<>(Arrays.asList(B)));
groups.add(new HashSet<>(Arrays.asList(C)));
groups.add(new HashSet<>(Arrays.asList(D)));
groups.add(new HashSet<>(Arrays.asList(E)));
groups.add(new HashSet<>(Arrays.asList(F)));
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
private static Set<Set<String>> mergeSubsets(List<Set<String>> groups) {
List<Set<String>> newGroups = new ArrayList<>();
Set<String> init = groups.get(0);
groups.remove(0);
newGroups.add(init);
while (!groups.isEmpty()) {
removeMergedElementFromGroupAndUpdateNewGroup(newGroups.get(newGroups.size() - 1), groups);
if(!groups.isEmpty()) {
init = groups.get(0);
groups.remove(0);
newGroups.add(init);
}
}
return new HashSet<>(newGroups);
}
private static void removeMergedElementFromGroupAndUpdateNewGroup(Set<String> master2, List<Set<String>> masterList) {
Iterator<Set<String>> iterator = masterList.iterator();
while (iterator.hasNext()) {
Set<String> strings = iterator.next();
boolean merge = strings.stream().anyMatch(string -> master2.contains(string));
if (merge) {
master2.addAll(strings);
iterator.remove();
}
}
}
}
Hope this helps instead of Set<Set<String>> groups I have used List<Set<String>> groups for the ease of using lists if you have a constraint of using Set only , you can generate List from Set(say yourSet) by passing it into the constructor of Lists implementation , for eg.
groups = new ArrayList<>(yourSet);
2*2 Matrix
Men Friends
A B,C,D
B E,F
C A
E B
F B
G F
I need list of Friends and Friends of Friends for requested men.
Example Like G -> F,B,E,F,B and After removing duplicate F,B,E
I resolved it with loops and recursion but not satisfied
Need better approach/suggestion.. rest i will implement.
Why not try something like this. Of course, i have taken a bit of freedom on the design as you didnt provide any code. Hope this helps!
private static Set<Node> getNodesFollowAllEdges(Node node) {
Set<Node> nodes = getNodesFollowAllEdges(node, new HashSet<>());
// remember to remove the original node from the set
nodes.remove(node);
return nodes;
}
private static Set<Node> getNodesFollowAllEdges(Node node, Set<Node> visited) {
if (node.getConnectedNodes().isEmpty()) {
return visited;
}
for (Node n : node.getConnectedNodes()) {
if (!visited.contains(n)) {
visited.add(n);
getNodesFollowAllEdges(n, visited);
}
}
return visited;
}
Also, it is very easy to provide a maximum search dept. Just add int maxDept and increase it every recursion step.
Given the following example:
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
Node f = new Node("F");
Node g = new Node("G");
a.addConnectedNodes(b, c, d);
b.addConnectedNodes(e, f);
c.addConnectedNodes(a);
e.addConnectedNodes(b);
f.addConnectedNodes(b);
g.addConnectedNodes(f);
Set<Node> friends = getNodesFollowAllEdges(a);
friends.forEach(node -> System.out.println(node.getName()));
should give you the correct result of (order neglected)
B
F
E
Note: Remember that, since its a Set, the resulting nodes can be in any order.
Thanks Glains,
My Code is :
public class Demo {
Set<String> freinds = new HashSet<>();
public static void main(String[] args) {
String[][] emailArray = new String[][] {
{ "abc#gmail.com", "abc1#gmail.com,abc2#gmail.com,abc3#gmail.com,1212#gmail.com" },
{ "abc1#gmail.com", "bcs1#gmail.combc2#gmail.com,bcds3#gmail.com" },
{ "bc#gmail.com", "bc1#gmail.combc2#gmail.com,bc3#gmail.com" } };
new Demo().sendMail("#gmail.combc2#gmail.com", "sdsd", emailArray);
}
void sendMail(String email, String message, String[][] freindsArray) {
Map<String, Email> emailsMap = new HashMap<>();
for (int i = 0; i < freindsArray.length; i++) {
for (int j = 0; j < 1; j++) {
Email e = new Email(freindsArray[i][j]);
e.addConnectedNodes(freindsArray[i][j + 1]);
emailsMap.put(e.email, e);
}
}
if (emailsMap.containsKey(email)) {
Demo.getNodesFollowAllEdges(emailsMap.get(email), emailsMap).forEach(e -> {
System.out.println(e);
});
} else {
System.out.println("no emails exist");
}
}
private static Set<String> getNodesFollowAllEdges(Email e, Map<String, Email> emailsMap) {
Set<String> nodes = getNodesFollowAllEdges(e, new HashSet<>(), emailsMap);
nodes.remove(e.email);
return nodes;
}
private static Set<String> getNodesFollowAllEdges(Email node, Set<String> visited, Map<String, Email> emailsMap) {
if (node.getConnectedEmails().isEmpty()) {
return visited;
}
for (String n : node.getConnectedEmails()) {
if (!visited.contains(n)) {
visited.add(n);
if (emailsMap.get(n) != null) {
getNodesFollowAllEdges(emailsMap.get(n), visited, emailsMap);
}
}
}
return visited;
}
}
class Email {
String email;
List<String> freindsEmails = new ArrayList<>();
public List<String> getConnectedEmails() {
return freindsEmails;
}
public Email(String email) {
this.email = email;
}
public void addConnectedNodes(String friendsEmail) {
freindsEmails.addAll(Arrays.asList(friendsEmail.split(",")));
}
}
I need to union a collection of sets by intersection of sets and write a function with such signature
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection);
Here is a simple example of sets
1) {1,2,3}
2) {4}
3) {1,5}
4) {4,7}
5) {3,5}
In this example we can see that sets 1, 3, and 5 intersect. We can rewrite it as a new set {1,2,3,5}. Also we have two sets that have intersections as well. They're 2 and 4, and we can create a new set {4,7}. The output result will be a collection of two sets: {1,2,3,5} and {4,7}.
I don't know from which point to start solving this task.
This should solve your use-case. It may be implemented in a more efficient way, but I guess this should give you an idea to start with:
private static Collection<Set<Integer>> mergeIntersections(Collection<Set<Integer>> collection) {
Collection<Set<Integer>> processedCollection = mergeIntersectionsInternal(collection);
while (!isMergedSuccessfully(processedCollection)) {
processedCollection = mergeIntersectionsInternal(processedCollection);
}
return processedCollection;
}
private static boolean isMergedSuccessfully(Collection<Set<Integer>> processedCollection) {
if (processedCollection.size() <= 1) {
return true;
}
final Set<Integer> mergedNumbers = new HashSet<>();
int totalNumbers = 0;
for (Set<Integer> set : processedCollection) {
totalNumbers += set.size();
mergedNumbers.addAll(set);
}
if (totalNumbers > mergedNumbers.size()) {
return false;
}
return true;
}
private static Collection<Set<Integer>> mergeIntersectionsInternal(Collection<Set<Integer>> collection) {
final Collection<Set<Integer>> processedCollection = new ArrayList<>();
// ITERATE OVER ALL SETS
for (final Set<Integer> numberSet : collection) {
for (final Integer number : numberSet) {
boolean matched = false;
// ITERATE OVER ALL PROCESSED SETS COLLECTION
for (final Set<Integer> processedSet : processedCollection) {
// CHECK OF THERE IS A MATCH
if (processedSet.contains(number)) {
matched = true;
// MATCH FOUND, MERGE THE SETS
processedSet.addAll(numberSet);
// BREAK OUT OF PROCESSED COLLECTION LOOP
break;
}
}
// IF NOT MATCHED THEN ADD AS A COLLECTION ITEM
if (!matched) {
processedCollection.add(new HashSet<>(numberSet));
}
}
}
return processedCollection;
}
This is how it executed it:
public static void main(String[] args) {
final Collection<Set<Integer>> collection = new ArrayList<>();
final Set<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
collection.add(set1);
final Set<Integer> set2 = new HashSet<>();
set2.add(4);
collection.add(set2);
final Set<Integer> set3 = new HashSet<>();
set3.add(1);
set3.add(5);
collection.add(set3);
final Set<Integer> set4 = new HashSet<>();
set4.add(4);
set4.add(7);
collection.add(set4);
final Set<Integer> set5 = new HashSet<>();
set5.add(3);
set5.add(5);
collection.add(set5);
System.out.println(mergeIntersections(collection));
}
Here’s my go. It deletes all sets from the input collection, this could be easily fixed by making a copy first. It does not modify each set in the input collection. With my implementation Ajay’s main method prints [[1, 2, 3, 5], [4, 7]].
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
Collection<Set<Integer>> mergedSets = new ArrayList<>(collection.size());
// for each set at a time, merge it with all sets that intersect it
while (! collection.isEmpty()) {
// take out the first set; make a copy as not to mutate original sets
Set<Integer> currentSet = new HashSet<>(removeOneElement(collection));
// find all intersecting sets and merge them into currentSet
// the trick is to continue merging until we find no intersecting
boolean mergedAny;
do {
mergedAny = false;
Iterator<Set<Integer>> it = collection.iterator();
while (it.hasNext()) {
Set<Integer> candidate = it.next();
if (intersect(currentSet, candidate)) {
it.remove();
currentSet.addAll(candidate);
mergedAny = true;
}
}
} while (mergedAny);
mergedSets.add(currentSet);
}
return mergedSets;
}
private static Set<Integer> removeOneElement(Collection<Set<Integer>> collection) {
Iterator<Set<Integer>> it = collection.iterator();
Set<Integer> element = it.next();
it.remove();
return element;
}
/** #return true if the sets have at least one element in common */
private static boolean intersect(Set<Integer> leftSet, Set<Integer> rightSet) {
// don’t mutate, take a copy
Set<Integer> copy = new HashSet<>(leftSet);
copy.retainAll(rightSet);
return ! copy.isEmpty();
}
An elegant way to solve this problem is using Undirected Graphs, where you connect an element from an input set with at least one other element from the same set, and then look for the Connected Components.
So the graph representation of your example is:
And from that we can easily infer the Connected Components: {1, 2, 3, 5} and {4, 7}.
Here is my code:
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
// Build the Undirected Graph represented as an adjacency list
Map<Integer, Set<Integer>> adjacents = new HashMap<>();
for (Set<Integer> integerSet : collection) {
if (!integerSet.isEmpty()) {
Iterator<Integer> it = integerSet.iterator();
int node1 = it.next();
while (it.hasNext()) {
int node2 = it.next();
if (!adjacents.containsKey(node1)) {
adjacents.put(node1, new HashSet<>());
}
if (!adjacents.containsKey(node2)) {
adjacents.put(node2, new HashSet<>());
}
adjacents.get(node1).add(node2);
adjacents.get(node2).add(node1);
}
}
}
// Run DFS on each node to collect the Connected Components
Collection<Set<Integer>> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
for (int start : adjacents.keySet()) {
if (!visited.contains(start)) {
Set<Integer> resultSet = new HashSet<>();
Deque<Integer> stack = new ArrayDeque<>();
stack.push(start);
while (!stack.isEmpty()) {
int node1 = stack.pop();
visited.add(node1);
resultSet.add(node1);
for (int node2 : adjacents.get(node1)) {
if (!visited.contains(node2)) {
stack.push(node2);
}
}
}
result.add(resultSet);
}
}
return result;
}
IMHO the best solution is Union-Find algorithm
An implemtation:
public class UnionFind {
Set<Integer> all = new HashSet<>();
Set<Integer> representants = new HashSet<>();
Map<Integer, Integer> parents = new HashMap<>();
public void union(int p0, int p1) {
int cp0 = find(p0);
int cp1 = find(p1);
if (cp0 != cp1) {
int size0 = parents.get(cp0);
int size1 = parents.get(cp1);
if (size1 < size0) {
int swap = cp0;
cp0 = cp1;
cp1 = swap;
}
parents.put(cp0, size0 + size1);
parents.put(cp1, cp0);
representants.remove(cp1);
}
}
public int find(int p) {
Integer result = parents.get(p);
if (result == null) {
all.add(p);
parents.put(p, -1);
representants.add(p);
result = p;
} else if (result < 0) {
result = p;
} else {
result = find(result);
parents.put(p, result);
}
return result;
}
public Collection<Set<Integer>> getGroups() {
Map<Integer, Set<Integer>> result = new HashMap<>();
for (Integer representant : representants) {
result.put(representant, new HashSet<>(-parents.get(representant)));
}
for (Integer value : all) {
result.get(find(value)).add(value);
}
return result.values();
}
public static Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
UnionFind groups = new UnionFind();
for (Set<Integer> set : collection) {
if (!set.isEmpty()) {
Iterator<Integer> it = set.iterator();
int first = groups.find(it.next());
while (it.hasNext()) {
groups.union(first, it.next());
}
}
}
return groups.getGroups();
}
}
I'm working on Dijkstra's algorithm,and I need to find all possible shortest paths. Dijkstra's algorithm returns only one short path, if another path has the same cost I would like to print it. I'm out of ideas, please help me.
Thank you.
Here's my algorithm:
public class Dijkstra {
private static final Graph.Edge[] GRAPH = {
new Graph.Edge("a", "b", 7),
new Graph.Edge("a", "c", 9),
new Graph.Edge("a", "f", 14),
new Graph.Edge("b", "c", 10),
new Graph.Edge("b", "d", 13),
new Graph.Edge("c", "d", 11),
new Graph.Edge("c", "f", 2),
new Graph.Edge("d", "e", 6),
new Graph.Edge("e", "f", 9),
};
private static final String START = "a";
private static final String END = "e";
public static void main(String[] args) {
Graph g = new Graph(GRAPH);
g.dijkstra(START);
g.printPath(END);
//g.printAllPaths();
}
}
import java.io.*;
import java.util.*;
class Graph {
private final Map<String, Vertex>
graph; // mapping of vertex names to Vertex objects, built from a set of Edges
/** One edge of the graph (only used by Graph constructor) */
public static class Edge {
public final String v1, v2;
public final int dist;
public Edge(String v1, String v2, int dist) {
this.v1 = v1;
this.v2 = v2;
this.dist = dist;
}
}
/** One vertex of the graph, complete with mappings to neighbouring vertices */
public static class Vertex implements Comparable<Vertex> {
public final String name;
public int dist = Integer.MAX_VALUE; // MAX_VALUE assumed to be infinity
public Vertex previous = null;
public final Map<Vertex, Integer> neighbours = new HashMap<>();
public Vertex(String name) {
this.name = name;
}
private void printPath() {
if (this == this.previous) {
System.out.printf("%s", this.name);
} else if (this.previous == null) {
System.out.printf("%s(unreached)", this.name);
} else {
this.previous.printPath();
System.out.printf(" -> %s(%d)", this.name, this.dist);
}
}
public int compareTo(Vertex other) {
return Integer.compare(dist, other.dist);
}
}
/** Builds a graph from a set of edges */
public Graph(Edge[] edges) {
graph = new HashMap<>(edges.length);
//one pass to find all vertices
for (Edge e : edges) {
if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex(e.v1));
if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex(e.v2));
}
//another pass to set neighbouring vertices
for (Edge e : edges) {
graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
//graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph
}
}
/** Runs dijkstra using a specified source vertex */
public void dijkstra(String startName) {
if (!graph.containsKey(startName)) {
System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName);
return;
}
final Vertex source = graph.get(startName);
NavigableSet<Vertex> q = new TreeSet<>();
// set-up vertices
for (Vertex v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Integer.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
/** Implementation of dijkstra's algorithm using a binary heap. */
private void dijkstra(final NavigableSet<Vertex> q) {
Vertex u, v;
while (!q.isEmpty()) {
u = q.pollFirst(); // vertex with shortest distance (first iteration will return source)
if (u.dist == Integer.MAX_VALUE)
break; // we can ignore u (and any other remaining vertices) since they are unreachable
//look at distances to each neighbour
for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) {
v = a.getKey(); //the neighbour in this iteration
final int alternateDist = u.dist + a.getValue();
if (alternateDist < v.dist) { // shorter path to neighbour found
q.remove(v);
v.dist = alternateDist;
v.previous = u;
q.add(v);
} else if (alternateDist == v.dist) {
// Here I Would do something
}
}
}
}
/** Prints a path from the source to the specified vertex */
public void printPath(String endName) {
if (!graph.containsKey(endName)) {
System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName);
return;
}
graph.get(endName).printPath();
System.out.println();
}
/** Prints the path from the source to every vertex (output order is not guaranteed) */
public void printAllPaths() {
for (Vertex v : graph.values()) {
v.printPath();
System.out.println();
}
}
public void printAllPaths2() {
graph.get("e").printPath();
System.out.println();
}
}
Have a look into so called k-shortest path algorithms. These solve the problem of enumerating the first, second, ..., kth shortest path in a graph. There are several algorithms in the literature, see for example this paper, or Yen's algorithm.
Note, that most algorithms do not require that you specify k upfront, ie. you can use them to enumerate shortest paths in an increasing order, and stop when the length has strictly increased.