Как рекурсивно объединить подмножества в список в Python?

У меня есть список наборов:

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? Как рекурсивно объединить подмножества в список в Python?

Я думаю, что мне следует применить какой-то рекурсивный подход, но я не мог придумать, как это сделать.

У вас есть список списков, а не список наборов.

Barmar 04.06.2024 19:52

Могут ли существовать дальнейшие уровни вложенности? Если нет, то я не думаю, что вам нужна рекурсия.

Barmar 04.06.2024 19:52

Перебираем элементы списка. Проверьте, не пересекается ли он с каким-либо элементом списка результатов. Если да, объедините его с этим элементом. В противном случае добавьте его в список результатов.

Barmar 04.06.2024 19:54

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

Barmar 04.06.2024 19:56

Покажите, пожалуйста, что вы пробовали. Я могу дать вам советы по дизайну и помочь исправить ваш код, но мы не будем писать его за вас.

Barmar 04.06.2024 19:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
58
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Можно взять два комплекта и уменьшить его:


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))

Другие вопросы по теме