Начиная с набора наборов «группы»:
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}}
Любые два произвольных подмножества, состоящие из ненулевого пересечения, должны быть объединены. Я считаю, что это поддается некоторому рекурсивному решению, но мне трудно понять это.
решите это на листе бумаги - за этим явно стоит алгоритм. затем напишите код Java
или воспользуйтесь поиском, здесь уже является решением на основе Python
Как в вашем результирующем наборе есть два c?
@CoffeeIsProgramming Мой другой вопрос все еще остается без ответа, какова логика слияния наборов. Почему бы не объединить их все в одно? Или почему не 1-й и 3-й в вашем более позднем примере?
Почему бы не объединить их все в одно? Потому что это не требование. Почему не первое и третье в моем последнем примере? Потому что это не требование. Пересечение {a, b, c} и {m, n, o} пусто. Я хочу создать новый список наборов, объединив все подмножества с «общими» элементами
Хорошо, я понял, а как насчет {{a, b, c}, {c, d, e, f}, {e, n, o}}, каков будет результат?
Не беспокойтесь, вывод будет следующим: {{a, b, c, d, e, f, n, o}} Он будет рекурсивно цепочкой, потому что наборы 1 и 2 имеют общий элемент c, а наборы 2 и 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"};
Логика в порядке. Отсутствовал пустой чек. Добавили этот чек. Спасибо, что указали на промах
Вы можете просто реализовать алгоритм, как вы его описываете в своей постановке задачи - найти пересекающиеся множества и объединять их, пока не останется нечего объединять. В стандартной библиотеке есть метод 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;
}
Чего вы так же пытались добиться? Какова логика группировки Set1,2,3