-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
ConnectedComponentsAdjacencyList.java
167 lines (132 loc) · 4.46 KB
/
ConnectedComponentsAdjacencyList.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* This file contains an algorithm to find all the connected components of an undirected graph. If
* the graph you're dealing with is directed have a look at Tarjan's algorithm to find strongly
* connected components.
*
* <p>The approach I will use to find all the strongly connected components is to use a union find
* data structure to merge together nodes connected by an edge. An alternative approach would be to
* do a breadth first search from each node (except the ones already visited of course) to determine
* the individual components.
*
* @author William Fiset, william.alexandre.fiset@gmail.com
*/
package com.williamfiset.algorithms.graphtheory;
import java.util.*;
public class ConnectedComponentsAdjacencyList {
static class Edge {
int from, to, cost;
public Edge(int from, int to, int cost) {
this.from = from;
this.to = to;
this.cost = cost;
}
}
static int countConnectedComponents(Map<Integer, List<Edge>> graph, int n) {
UnionFind uf = new UnionFind(n);
for (int i = 0; i < n; i++) {
List<Edge> edges = graph.get(i);
if (edges != null) {
for (Edge edge : edges) {
uf.unify(edge.from, edge.to);
}
}
}
return uf.components();
}
// Finding connected components example
public static void main(String[] args) {
final int numNodes = 7;
Map<Integer, List<Edge>> graph = new HashMap<>();
// Setup a graph with four connected components
// namely: {0,1,2}, {3,4}, {5}, {6}
addUndirectedEdge(graph, 0, 1, 1);
addUndirectedEdge(graph, 1, 2, 1);
addUndirectedEdge(graph, 2, 0, 1);
addUndirectedEdge(graph, 3, 4, 1);
addUndirectedEdge(graph, 5, 5, 1); // Self loop
int components = countConnectedComponents(graph, numNodes);
System.out.printf("Number of components: %d\n", components);
}
// Helper method to setup graph
private static void addUndirectedEdge(
Map<Integer, List<Edge>> graph, int from, int to, int cost) {
List<Edge> list = graph.get(from);
if (list == null) {
list = new ArrayList<Edge>();
graph.put(from, list);
}
list.add(new Edge(from, to, cost));
list.add(new Edge(to, from, cost));
}
}
// Union find data structure
class UnionFind {
// The number of elements in this union find
private int size;
// Used to track the sizes of each of the components
private int[] sz;
// id[i] points to the parent of i, if id[i] = i then i is a root node
private int[] id;
// Tracks the number of components in the union find
private int numComponents;
public UnionFind(int size) {
if (size <= 0) throw new IllegalArgumentException("Size <= 0 is not allowed");
this.size = numComponents = size;
sz = new int[size];
id = new int[size];
for (int i = 0; i < size; i++) {
id[i] = i; // Link to itself (self root)
sz[i] = 1; // Each component is originally of size one
}
}
// Find which component/set 'p' belongs to, takes amortized constant time.
public int find(int p) {
// Find the root of the component/set
int root = p;
while (root != id[root]) root = id[root];
// Compress the path leading back to the root.
// Doing this operation is called "path compression"
// and is what gives us amortized constant time complexity.
while (p != root) {
int next = id[p];
id[p] = root;
p = next;
}
return root;
}
// Return whether or not the elements 'p' and
// 'q' are in the same components/set.
public boolean connected(int p, int q) {
return find(p) == find(q);
}
// Return the size of the components/set 'p' belongs to
public int componentSize(int p) {
return sz[find(p)];
}
// Return the number of elements in this UnionFind/Disjoint set
public int size() {
return size;
}
// Returns the number of remaining components/sets
public int components() {
return numComponents;
}
// Unify the components/sets containing elements 'p' and 'q'
public void unify(int p, int q) {
int root1 = find(p);
int root2 = find(q);
// These elements are already in the same group!
if (root1 == root2) return;
// Merge smaller component/set into the larger one.
if (sz[root1] < sz[root2]) {
sz[root2] += sz[root1];
id[root1] = root2;
} else {
sz[root1] += sz[root2];
id[root2] = root1;
}
// Since the roots found are different we know that the
// number of components/sets has decreased by one
numComponents--;
}
}