culebrón
culebrón

Reputation: 36473

What's the pythonic way to unite all the intersecting sets in a list?

I have a list of sets,

[set([0, 1, 2]),
 set([3, 2]),
 set([4, 1]),
 set([5, 6]),
 set([7, 8])]

and I need to unite all those that are intersecting, so that the result is the following:

[set([0, 1, 2, 3, 4]),
 set([5, 6]),
 set([7, 8])]

What's the most elegant way to do this? I can't think of anything better than n*n cycle.

Upvotes: 4

Views: 452

Answers (2)

Wilduck
Wilduck

Reputation: 14116

I'd do something like this:

sets = [set([0, 1, 2]),
        set([3, 2]),
        set([4, 1]),
        set([5, 6]),
        set([7, 8])]

intersections = [sets[0]]
for start_set in sets[1:]:
    intersects = False
    for final_set in intersections:
        if len(final_set & start_set) > 0:
            final_set.update(start_set)
            intersects = True
    if not intersects:
        intersections.append(start_set)

It should be O(n) in the best case (all sets intersect) but will be O(n*n) in the worst case (no sets intersect).

Upvotes: 1

g.d.d.c
g.d.d.c

Reputation: 47988

This would produce the output you describe. It should run better than n * n unless you have no intersections, but otherwise should benefit some.

mysets = [set([0, 1, 2]),
          set([3, 2]),
          set([4, 1]),
          set([5, 6]),
          set([7, 8])]

# Require at least one set in the output.
output = [mysets.pop(0)]

while mysets:
  test = mysets.pop(0)
  for idx, other in enumerate(output):
    if test & other:
      output[idx] |= test
      break
  else:
    output.append(test)

# output -> [set([0, 1, 2, 3, 4]), set([5, 6]), set([8, 7])]

Upvotes: 4

Related Questions