У меня есть список наборов:
my_set = [[1, 2, 3], [1, 5], [4, 5, 6], [7, 8]]
Я хочу объединить все подмножества, имеющие общие элементы (хотя бы с одним подмножеством) в этом списке, в одно подмножество:
my_final_set = [[1, 2, 3, 4, 5, 6], [7, 8]]
В этом случае элементы 1, 2, 3, 4, 5, 6 должны быть сгруппированы в одно итоговое подмножество, как показано на изображении. Как я могу сделать это на Python?

Я думаю, что мне следует применить какой-то рекурсивный подход, но я не мог придумать, как это сделать.
Могут ли существовать дальнейшие уровни вложенности? Если нет, то я не думаю, что вам нужна рекурсия.
Перебираем элементы списка. Проверьте, не пересекается ли он с каким-либо элементом списка результатов. Если да, объедините его с этим элементом. В противном случае добавьте его в список результатов.
Если вам нужно что-то более эффективное, вы можете создать словарь, ключи которого являются элементами внутреннего списка, а значения — элементом списка результатов, содержащим это число. Затем, чтобы определить, есть ли перекрытие, вы можете просто проверить, является ли какой-либо из элементов внутреннего списка уже ключом.
Покажите, пожалуйста, что вы пробовали. Я могу дать вам советы по дизайну и помочь исправить ваш код, но мы не будем писать его за вас.






Можно взять два комплекта и уменьшить его:
def get_union(sets):
def _rec(S, B):
A = set(S)
i = 0
while i < len(B):
if A.intersection(B[i]):
A.update(B.pop(i))
A, B = _rec(A, B)
else:
i += 1
return A, B
res = []
while sets:
C, sets = _rec(sets[0], sets[1:])
res.append(list(C))
return res
print(get_union([[1, 2, 3], [1, 5], [4, 5, 6], [7, 8]]))
[[1, 2, 3, 4, 5, 6], [8, 7]]
Кажется, это идеальный кандидат для структуры данных непересекающегося множества. Примените операцию объединения, чтобы связать номера, принадлежащие одному набору. Как только это будет сделано для всех наборов, получите результат, прочитав приведенную выше структуру данных:
# Implementation of Union-Find (Disjoint Set)
class Node:
def __init__(self):
self.parent = self
self.rank = 0
def find(self):
if self.parent.parent != self.parent:
self.parent = self.parent.find()
return self.parent
def union(self, other):
node = self.find()
other = other.find()
if node == other:
return # was already in same set
if node.rank > other.rank:
node, other = other, node
node.parent = other
other.rank = max(other.rank, node.rank + 1)
from collections import defaultdict
def merge(sets):
nodes = defaultdict(Node)
for first, *rest in sets:
for item in rest:
nodes[item].union(nodes[first])
merged = defaultdict(list)
for item, node in nodes.items():
merged[node.find()].append(item)
return list(merged.values())
print(merge([[1, 2, 3], [1, 5], [4, 5, 6], [7, 8]]))
Я бы предложил использовать один collections.Counter для отслеживания элементов, которые появляются более чем в одном наборе, а затем использовать его для сортировки наборов на перекрывающиеся и непересекающиеся. Вот быстрая демонстрация:
from collections import Counter
my_sets = [{1, 2, 3}, {1, 5}, {4, 5, 6}, {7, 8}]
counts = Counter(i for s in my_sets for i in s)
merged_set = set()
remainder = []
for s in my_sets:
if any(counts[i] > 1 for i in s):
merged_set |= s
else:
remainder.append(s)
дает нам:
>>> [merged_set] + remainder
[{1, 2, 3, 4, 5, 6}, {8, 7}]
Обратите внимание, что это зависит от того, является ли my_sets набором наборов, а не списками, как в вашем исходном вопросе. Если это набор произвольных списков, измените Counter так, чтобы он учитывал только один элемент в каждом подсписке, превратив его в набор перед подсчетом:
counts = Counter(i for s in my_sets for i in set(s))
У вас есть список списков, а не список наборов.