I have a graph which contains an unknown number of disconnected subgraphs. What's a good algorithm (or Java library) to find them all?
I have a graph which contains an unknown number of disconnected subgraphs. What's a good algorithm (or Java library) to find them all?
I probably should have found a standard algorithm (Wikipedia has some suggestions), but I came up with this on my own and it worked well for my purposes. My C# implementation takes a couple seconds to process a graph with 40,000 nodes and 44,000 edges to find 160 subgraphs and 20,000 unconnected nodes. I used a HashSet to store each subgraph group, so testing group membership is approximately O(1), and the overall algorithm becomes O(E*C) where E is the number of edges and C is the number of connected components in the graph. Wikipedia mentions an O(N) algorithm, linear in the number of nodes, so I'm sure mine is not maximally efficient, but it was plenty fast for my application. (Edit: I'm also glossing over the cost of merging groups, so don't put too much weight on my complexity analysis.)
Logic:
For each edge A->B
If A is in a group and B is not, add B to group A
If A is not in a group and B is, add A to group B
If A and B are in different groups, merge the groups
If neither A or B is in a group, create new group containing A and B
Pseudocode:
graph = {nodes, edges}
groups = {}
foreach edge A->B in graph.edges:
groupA = findgroup(A)
groupB = findgroup(B)
if (groupA && !groupB)
groupA.add(B)
elif (!groupA && groupB)
groupB.add(A)
elif (groupA && groupB)
if (groupA != groupB)
groupA.union(groupB)
groups.delete(groupB)
else
groups.add({A,B})
findgroup(node):
for group in groups:
if group.contains(node):
return group
return null
Note that this won't capture any nodes that aren't involved in edges. For my particular purposes this was fine. If you want to get all the single-node groups you can do a final pass:
foreach node in graph.nodes:
group = findgroup(node)
if !group:
groups.add({node})