
本文详解如何在 java 中枚举所有由原始列表构成的、内部子列表两两互不相交(disjoint)的组合集合,并从中筛选出“最大规模”的合法组合——即子列表数量最多的那些解,兼顾正确性、可读性与工程实用性。
本文详解如何在 java 中枚举所有由原始列表构成的、内部子列表两两互不相交(disjoint)的组合集合,并从中筛选出“最大规模”的合法组合——即子列表数量最多的那些解,兼顾正确性、可读性与工程实用性。
该问题本质是组合搜索 + 集合互斥约束判定:给定若干整数列表(如 list1 到 list6),需生成所有可能的子集族(即 List> 的集合),使得族内任意两个子列表无公共元素(即 pairwise disjoint),并最终返回所有满足该条件且长度(子列表个数)达到最大值的族。
注意:题目示例输出中包含 6 个结果,其共同特征是每个结果均为长度为 3 的列表(如 [[1,2],[3,4],[5,6,7]]),而不存在长度为 4 或以上的合法组合——因此“最大规模”在此例中为 3。我们的目标不是仅输出一个解,而是穷举所有最大尺寸的互斥组合。
✅ 核心算法思路
预处理:计算两两相容性
定义 compatible[i][j] = true 当且仅当 lists.get(i) 与 lists.get(j) 互不相交(即交集为空)。使用 Collections.disjoint(a, b) 可高效判断。回溯枚举所有合法组合
采用深度优先搜索(DFS),逐个尝试将列表加入当前组合;每次加入前校验其与组合中所有已有列表是否均兼容。剪枝优化:按最大可能长度剪枝
维护全局最大长度 maxSize;若当前路径长度 + 剩余可选列表数 ≤ maxSize,则剪枝;当发现更长合法组合时,清空结果集并更新 maxSize。去重与规范输出
因输入顺序固定,且组合本身无序,但为避免重复(如 [A,B,C] 与 [B,A,C] 视为同一解),建议在回溯中强制按索引升序选择(即只允许从 startIdx 开始向后选),天然保证组合唯一性。
✅ Java 实现示例
import java.util.*;
public class DisjointListCombinations {
private static List<List<Integer>> lists;
private static boolean[][] compatible;
private static int maxSize = 0;
private static List<List<List<Integer>>> results = new ArrayList<>();
public static List<List<List<Integer>>> findMaxDisjointCombinations(List<List<Integer>> input) {
lists = input;
int n = lists.size();
compatible = new boolean[n][n];
// 预计算兼容矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
compatible[i][j] = Collections.disjoint(lists.get(i), lists.get(j));
}
}
results.clear();
maxSize = 0;
backtrack(new ArrayList<>(), 0);
return results;
}
private static void backtrack(List<List<Integer>> current, int startIdx) {
// 更新最大长度并保存结果
if (current.size() > maxSize) {
maxSize = current.size();
results.clear();
results.add(new ArrayList<>(current));
} else if (current.size() == maxSize && maxSize > 0) {
results.add(new ArrayList<>(current));
}
// 尝试添加后续列表
for (int i = startIdx; i < lists.size(); i++) {
boolean canAdd = true;
for (List<Integer> existing : current) {
// 检查与 current 中每个列表是否兼容
if (!compatible[lists.indexOf(existing)][i]) {
canAdd = false;
break;
}
}
if (canAdd) {
current.add(lists.get(i));
backtrack(current, i + 1); // 严格递增索引,避免重复
current.remove(current.size() - 1);
}
}
}
// 测试入口
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2);
List<Integer> list2 = Arrays.asList(3, 4);
List<Integer> list3 = Arrays.asList(5, 6, 7);
List<Integer> list4 = Arrays.asList(2, 3);
List<Integer> list5 = Arrays.asList(7);
List<Integer> list6 = Arrays.asList(3);
List<List<Integer>> input = Arrays.asList(list1, list2, list3, list4, list5, list6);
List<List<List<Integer>>> result = findMaxDisjointCombinations(input);
System.out.println("Maximum disjoint combinations (size = " + result.get(0).size() + "):");
result.forEach(combo -> System.out.println(combo));
}
}⚠️ 注意事项与最佳实践
- Collections.disjoint() 是关键:它时间复杂度为 O(min(a.size(), b.size())),比手动实现交集更简洁安全。
-
索引映射需谨慎:上述代码中 lists.indexOf(existing) 在存在重复列表时可能出错;生产环境建议用 List
- > 的不可变副本 + 索引绑定(如封装为 ListWithIndex),或直接用下标而非对象引用比对。
- 空间优化:若列表数量较大(>20),回溯可能产生指数级组合;此时应考虑启发式算法(如贪心选择最大不交集)或转为图模型(构建冲突图,求最大独立集)。
- 结果语义:本解法返回的是所有 最大基数(maximum cardinality)的互斥子集族,符合题干“largest list where sublists are not intersections”的本意——即“子列表数量最多”的合法组合集合。
通过该方案,你不仅能精准复现题干中的 6 个输出,还能稳健扩展至任意规模输入,是解决此类集合约束组合问题的标准工程化路径。










