如何修改递归方法实现多列表指定顺序的笛卡尔积生成?
问题描述
我正在处理以下3个列表:
List<String> first = Arrays.asList("a", "b"); List<String> second = Arrays.asList("X", "Y", "Z"); List<String> third = Arrays.asList("1", "2");
我用来生成所有组合的递归方法如下:
public static void permute(List<List<String>> lists, List<List<String>> result, int depth, String current) { if (depth == lists.size()) { List<String> current_list = new ArrayList<>(); current_list = current.chars().mapToObj(e -> Character.toString((char)e)) .collect(Collectors.toList()); result.add(current_list); return; } for (int i = 0; i < lists.get(depth).size(); i++) { permute(lists, result, depth + 1, current + lists.get(depth).get(i)); } }
该方法当前输出结果为:
[[a, X, 1], [a, X, 2], [a, Y, 1], [a, Y, 2], [a, Z, 1], [a, Z, 2], [b, X, 1], [b, X, 2], [b, Y, 1], [b, Y, 2], [b, Z, 1], [b, Z, 2]]
请问如何修改该方法,使其输出如下指定顺序的结果:
[[a,X, 1], [b, X, 1], [a, Y, 1], [b, Y, 1], [a, Z, 1], [b, Z, 1], [a, X, 2], [b, X, 2], [a, Y, 2], [b, Y, 2], [a, Z, 2], [b, Z, 2]]
解决方案
当前递归是按第一个列表→第二个→第三个的顺序深度优先遍历,导致输出以第一个列表元素分组。而你需要的是第三个列表→第二个→第一个的遍历优先级,也就是把组合的排序维度反转。
推荐修改方案(更健壮)
原方法用字符串拼接收集元素,若元素长度大于1会出现拆分错误,建议直接用List<String>保存当前组合,同时调整递归的遍历顺序:
public static void permute(List<List<String>> lists, List<List<String>> result, int depth, List<String> current) { // 所有层级元素选择完成,保存结果 if (depth == 0) { result.add(new ArrayList<>(current)); return; } // 从后往前取要处理的列表 int targetListIdx = lists.size() - depth; for (String elem : lists.get(targetListIdx)) { current.add(elem); // 递归处理前一层(depth递减) permute(lists, result, depth - 1, current); current.remove(current.size() - 1); // 回溯 } }
调用方式:
List<List<String>> lists = Arrays.asList(first, second, third); List<List<String>> result = new ArrayList<>(); permute(lists, result, lists.size(), new ArrayList<>());
原理说明
- 反转遍历顺序:递归从最后一个列表开始(
depth从列表总数递减到0),优先遍历第三个列表的"1"、"2",再遍历第二个列表的"X"、"Y"、"Z",最后遍历第一个列表的"a"、"b",完全匹配你要的输出顺序。 - 用List替代字符串拼接:避免了单字符拆分的局限性,回溯逻辑也更清晰。
兼容原字符串拼接的修改方案(不推荐)
如果坚持用原方法的字符串拼接逻辑,可调整遍历的列表索引:
public static void permute(List<List<String>> lists, List<List<String>> result, int depth, String current) { if (depth == lists.size()) { List<String> current_list = new ArrayList<>(); current_list = current.chars().mapToObj(e -> Character.toString((char)e)) .collect(Collectors.toList()); result.add(current_list); return; } // 从后往前选择当前要遍历的列表 int targetListIdx = lists.size() - 1 - depth; for (int i = 0; i < lists.get(targetListIdx).size(); i++) { permute(lists, result, depth + 1, current + lists.get(targetListIdx).get(i)); } }
调用方式保持和原方法一致:
permute(lists, result, 0, "");
注意:此方案仅适用于所有元素都是单字符的场景,元素长度大于1时会出错。
内容的提问来源于stack exchange,提问作者Javadkhan
相关产品推荐
相关产品推荐

