I need to check how many separate graphs will be created where "n rows contain pairs of positive integers, where each pair identifies a connection between two vertices in the graph.". Suppose we have 3 pairs: [4 3], [1 4], [5 6]. The answer is 2, because [4 3]&[1 4] will be merged into one graph: [1 3 4], the second one is [5 6]. If we add another pair: [4 3], [1 4], [5 6], [4 6], the answer will be 1 (only 1 graph: [1 3 4 5 6] connected). p>
I managed to solve this problem using java, but it was not efficient and was very slow for more than 10000 pairs. The code looks more or less like this:
Set<Pair> connectionsSet; HashSet<TreeSet<Integer>> createdConnections; public void createGraphs() { for (Pair pair : connectionsSet) { boolean foundLeft = false, foundRight = false; for (TreeSet<Integer> singleSet : createdConnections) { if (singleSet.contains(pair.getLeft())) foundLeft = true; if (singleSet.contains(pair.getRight())) foundRight = true; } if (!foundLeft && !foundRight) addNewGraph(pair); else if (foundLeft && !foundRight) addToExistingGraph(pair, Constants.LEFT); else if (!foundLeft && foundRight) addToExistingGraph(pair, Constants.RIGHT); else if (foundLeft && foundRight) mergeGraphs(pair); } } private void addNewGraph(Pair pair) { createdConnections.add(new TreeSet<>(pair.asList())); } private void addToExistingGraph(Pair pair, String side) { for (TreeSet<Integer> singleSet : createdConnections) { if (side.equals(Constants.LEFT) && singleSet.contains(pair.getLeft())) singleSet.add(pair.getRight()); if (side.equals(Constants.RIGHT) && singleSet.contains(pair.getRight())) singleSet.add(pair.getLeft()); } } private void mergeGraphs(Pair pair) { Optional<TreeSet<Integer>> leftSetOptional = getOptional(pair.getLeft()); Optional<TreeSet<Integer>> rightSetOptional = getOptional(pair.getRight()); if (leftSetOptional.isPresent() && rightSetOptional.isPresent()){ TreeSet<Integer> leftSet = leftSetOptional.get(); TreeSet<Integer> rightSet = rightSetOptional.get(); rightSet.addAll(leftSet); createdConnections.removeIf(singleSet -> singleSet.contains(pair.getLeft())); createdConnections.removeIf(singleSet -> singleSet.contains(pair.getRight())); createdConnections.add(rightSet); } }
How to improve performance? I'm not asking for an off-the-shelf solution, but maybe there's an algorithm I'm not aware of that would speed things up significantly?
To get the number of connected components you can use disjoint sets. This is a simple implementation assuming the input is a list
of edges.
map<integer, integer> parent; int find(int x) { int p = parent.getordefault(x, x); if (p != x) p = find(p); parent.put(x, p); return p; } public int numconnectedcomponents(list<pair> edges) { parent = new hashmap<>(); for (var e : edges) { int lpar = find(e.getleft()), rpar = find(e.getright()); parent.put(lpar, rpar); } int comps = 0; for (var k : parent.keyset()) if (find(k) == k) ++comps; return comps; }
If the number of nodes (n
) is known, and we can assume that all node labels are integers between 1
and n
, we can pass Optimize using arrays instead of map
and keep track of the number of connected components as edges are added.
int[] parent; int find(int x) { return x == parent[x] ? x : (parent[x] = find(parent[x])); } public int numConnectedComponents(int nodes, List<Pair> edges) { parent = new int[nodes + 1]; int comps = 0; for (var e : edges) { if (parent[e.getLeft()] == 0) { parent[e.getLeft()] = e.getLeft(); ++comps; } if (parent[e.getRight()] == 0) { parent[e.getRight()] = e.getRight(); ++comps; } int lPar = find(e.getLeft()), rPar = find(e.getRight()); if (lPar != rPar) { parent[lPar] = rPar; --comps; } } return comps; }
The above is the detailed content of Build a graph based on connected pairs of vertices. For more information, please follow other related articles on the PHP Chinese website!