Java 8 - объединить все подмножества, содержащие общие элементы

Начиная с набора наборов «группы»:

Set<Set<String>> groups = new HashSet<>();

Я хочу создать новый список наборов, объединив все подмножества с общими элементами:

т.е. начиная с наборов ниже:

A = {a, b, c}
B = {c, d, e, f}
C = {f, g, h, i, j}
D = {k, l, m}
E = {m, n, o}
F = {p, q, r}

Конечный результат будет:

Set 1 = {a, b, c, d, e, f, g, h, i, j}
Set 2 = {k, l, m, n, o}
Set 3 = {p, q, r}

Любые советы о том, как это сделать, будут оценены.

Обновлено: в случае неравных наборов он будет работать так же. Итак, если бы это был метод, он выглядел бы так псевдо:

public void doStuff(){

  Set<Set<String>> groups = {{a,b,c}, {c,d,e,f}, {m, n, o}}

  Set<Set<String>> newGroups = mergeSubsets(groups);

  System.out.println(newGroups);
}

public Set<Set<String>> mergeSubsets(Set<Set<String>> groups){

     //some operations

}

Консольный выход:

   New Groups: {{a,b,c,d,e,f}, {m, n, o}}

Чего вы так же пытались добиться? Какова логика группировки Set1,2,3

Naman 02.12.2018 05:14

Любые два произвольных подмножества, состоящие из ненулевого пересечения, должны быть объединены. Я считаю, что это поддается некоторому рекурсивному решению, но мне трудно понять это.

user1870035 02.12.2018 05:18

решите это на листе бумаги - за этим явно стоит алгоритм. затем напишите код Java

UninformedUser 02.12.2018 05:35

или воспользуйтесь поиском, здесь уже является решением на основе Python

UninformedUser 02.12.2018 05:37

Как в вашем результирующем наборе есть два c?

shmosel 02.12.2018 05:44

@CoffeeIsProgramming Мой другой вопрос все еще остается без ответа, какова логика слияния наборов. Почему бы не объединить их все в одно? Или почему не 1-й и 3-й в вашем более позднем примере?

Naman 02.12.2018 06:14

Почему бы не объединить их все в одно? Потому что это не требование. Почему не первое и третье в моем последнем примере? Потому что это не требование. Пересечение {a, b, c} и {m, n, o} пусто. Я хочу создать новый список наборов, объединив все подмножества с «общими» элементами

user1870035 02.12.2018 06:24

Хорошо, я понял, а как насчет {{a, b, c}, {c, d, e, f}, {e, n, o}}, каков будет результат?

Naman 02.12.2018 06:33

Не беспокойтесь, вывод будет следующим: {{a, b, c, d, e, f, n, o}} Он будет рекурсивно цепочкой, потому что наборы 1 и 2 имеют общий элемент c, а наборы 2 и 3 имеют элемент ' е 'в общем.

user1870035 02.12.2018 06:36
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
9
634
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

import java.util.*;

public class MergeSet {
    public static void main(String... args) {
        List<Set<String>> groups = new ArrayList<>();
        String[] A = {"a", "b", "c"};
        String[] B = {"c", "d", "e", "f"};
        String[] C = {"f", "g", "h", "i", "j"};
        String[] D = {"k", "l", "m"};
        String[] E = {"m", "n", "o"};
        String[] F = {"p", "q", "r"};

        groups.add(new HashSet<>(Arrays.asList(A)));
        groups.add(new HashSet<>(Arrays.asList(B)));
        groups.add(new HashSet<>(Arrays.asList(C)));
        groups.add(new HashSet<>(Arrays.asList(D)));
        groups.add(new HashSet<>(Arrays.asList(E)));
        groups.add(new HashSet<>(Arrays.asList(F)));

        Set<Set<String>> newGroups = mergeSubsets(groups);
        System.out.println(newGroups);

    }

    private static Set<Set<String>> mergeSubsets(List<Set<String>> groups) {
        List<Set<String>> newGroups = new ArrayList<>();
        Set<String> init = groups.get(0);
        groups.remove(0);
        newGroups.add(init);
        while (!groups.isEmpty()) {
            removeMergedElementFromGroupAndUpdateNewGroup(newGroups.get(newGroups.size() - 1), groups);
            if (!groups.isEmpty()) {
                init = groups.get(0);
                groups.remove(0);
                newGroups.add(init);
            }
        }
        return new HashSet<>(newGroups);
    }

    private static void removeMergedElementFromGroupAndUpdateNewGroup(Set<String> master2, List<Set<String>> masterList) {
        Iterator<Set<String>> iterator = masterList.iterator();
        while (iterator.hasNext()) {
            Set<String> strings = iterator.next();
            boolean merge = strings.stream().anyMatch(string -> master2.contains(string));
            if (merge) {
                master2.addAll(strings);
                iterator.remove();
            }
        }
    }
}

Надеюсь, это поможет вместо Set<Set<String>> groups. Я использовал List<Set<String>> groups для простоты использования списков, если у вас есть ограничение на использование только Set, вы можете сгенерировать List из Set (скажем, yourSet), передав его в конструктор реализации Lists, например.

groups = new ArrayList<>(yourSet);

Это не работает для String[] A = {"a", "c", "e", "g"}; String[] B = {"b", "d", "f", "h"}; String[] C = {"c", "e", "f"}; String[] D = {"b"};

maloku 13.02.2019 11:48

Логика в порядке. Отсутствовал пустой чек. Добавили этот чек. Спасибо, что указали на промах

NiksVij 13.02.2019 16:44

Вы можете просто реализовать алгоритм, как вы его описываете в своей постановке задачи - найти пересекающиеся множества и объединять их, пока не останется нечего объединять. В стандартной библиотеке есть метод Collections.disjoint, который помогает определить, есть ли у двух коллекций какие-либо общие элементы:

// this implementation sacrifices efficiency for clarity
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups) {
    Set<Set<String>> result = new HashSet<>();
    for (Set<String> set : groups) {
        // try to find a set in result that intersects this set
        // if one is found, merge the two.  otherwise, add this set to result
        result.stream()
                .filter(x -> !Collections.disjoint(x, set))
                .findAny()
                .ifPresentOrElse(   // this method was added in java 9
                        x -> x.addAll(set),
                        () -> result.add(new HashSet<>(set))
                );
    }

    // if nothing got merged we are done; otherwise, recurse and try again
    return result.size() == groups.size() ? result : mergeSubsets(result);
}
Ответ принят как подходящий

Вот императивный способ, основанный на решении @NiksVij. Очевидно, что решение @NiksVij неверно, и этот ответ направлен на то, чтобы исправить это и немного расширить:

public class MergeSet {

    public static void main(String... args) {
        List<Set<String>> list = new ArrayList<>();
        String[] A = {"a", "c", "e", "g"};
        String[] B = {"b", "d", "f", "h"};
        String[] C = {"c", "e", "f"};
        String[] D = {"b"};

        list.add(new HashSet<>(Arrays.asList(A)));
        list.add(new HashSet<>(Arrays.asList(C)));
        list.add(new HashSet<>(Arrays.asList(B)));
        list.add(new HashSet<>(Arrays.asList(D)));

        List<Set<String>> newGroups = merge(list);
        System.out.println(newGroups);

    }

    @SuppressWarnings("empty-statement")
    private static <T> List<Set<T>> merge(List<Set<T>> list) {
        if (list == null || list.isEmpty()) {
            return list;
        }
        List<Set<T>> merged = new ArrayList<>();
        do {
            merged.add(list.get(0));
            list.remove(0);

            while (mergeStep(merged.get(merged.size() - 1), list));
        } while (!list.isEmpty());
        return merged;
    }

    private static <T> boolean mergeStep(Set<T> setToCheck, List<Set<T>> remainingList) {
        boolean atLeastOnceMerged = false;
        Iterator<Set<T>> iterator = remainingList.iterator();
        while (iterator.hasNext()) {
            Set<T> elements = iterator.next();
            boolean doMerge = !Collections.disjoint(elements, setToCheck);
            if (doMerge) {
                atLeastOnceMerged |= doMerge;
                setToCheck.addAll(elements);
                iterator.remove();
            }
        }
        return atLeastOnceMerged;
    }

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