给定一个大小为n的列表,编写一个程序,返回每个列表中包含的所有可能的元素组合。
示例:
输出:
订单并不重要,但困难的部分是:您不能使用递归。 我的解决方案:
void combos(const char *string)
{
int i, j, k;
int len = strlen(string);
for (i = 0; i < len - 2; i++)
{
for (j = i + 1; j < len - 1; j++)
{
for (k = j + 1; k < len; k++)
printf("%c%c%c\n", string[i], string[j], string[k]);
}
}
}
正如您所看到的,它只有在我知道手头的列表数量之后才有效。我很好奇,如果递归是解决它的唯一方法。
答案 0 :(得分:2)
您不需要递归。您需要做的就是建立一套中间解决方案。这是Python中的非递归解决方案:
# This does NOT use recursion!
def all_comb(list_of_lists):
# We start with a list of just the empty set.
answer = [[]]
for list in list_of_lists:
# new_answer will be the list of combinations including this one.
new_answer = []
# Build up the new answer.
for thing in list:
for prev_list in answer:
new_answer.append(prev_list + [thing])
# Replace the old answer with the new one.
answer = new_answer
# We now have all combinations of all lists.
return answer
# Demonstration that it works.
for comb in all_comb([["x", "y"], ["a", "b", "c"], ["o", "p"]]):
print(" ".join(comb))
答案 1 :(得分:2)
将其想象为如何增加数字,例如一个基数为3的数字:
000
001
002
010
011
...
222
现在想想每个数字都是每个嵌套列表的索引。您将拥有与嵌套列表一样多的数字,即外部列表的大小。
每个数字的“基数”可能不同,并且是相应嵌套列表的大小。如果嵌套列表很大,“数字”可以是一个非常大的数字。
因此,您首先要创建一个“数字”或索引值列表,将它们初始化为0
。然后,您可以在这些索引处打印元素的值。然后递增最后一个索引值,根据需要滚动,就像正常数字一样,在第一个索引值翻转时停止。
这是一个使用数组数组的Java实现,即String[][]
。如果需要,您可以轻松更改为List<List<String>>
或List<String[]>
。
@SafeVarargs
public static void printCombos(String[] ... lists) {
if (lists.length == 0)
throw new IllegalArgumentException("No lists given");
for (String[] list : lists)
if (list.length == 0)
throw new IllegalArgumentException("List is empty");
int[] idx = new int[lists.length];
for (;;) {
// Print combo
for (int i = 0; i < lists.length; i++) {
if (i != 0)
System.out.print(' ');
System.out.print(lists[i][idx[i]]);
}
System.out.println();
// Advance to next combination
for (int i = lists.length - 1; ++idx[i] == lists[i].length; ) {
idx[i] = 0;
if (--i < 0)
return; // We're done
}
}
}
public static void main(String[] args) {
String[][] data = { { "x", "z" }, { "a", "b", "c" }, { "o", "p" } };
printCombos(data);
}
<强>输出强>
x a o
x a p
x b o
x b p
x c o
x c p
z a o
z a p
z b o
z b p
z c o
z c p
如果您使用列表而不是数组,则代码将使用get(int)
,这可能并不总是有利于提高性能,例如LinkedList
。
如果是这种情况,请将int[] idx
替换为Iterator[]
,使用相应列表的迭代器初始化每个数组条目。然后,通过从相关列表中检索新的Iterator
,将“数字”重置为0。
在这种情况下,它们甚至不必是列表,但可以是任何类型的集合,或更具体地Iterable
个对象。
答案 2 :(得分:1)
编辑:我在python中回答这个问题,因为虽然它目前被标记为language-agnostic,但python是一个很好的可执行伪伪代码。
如果您可以使用 Tail-recursive 的形式编写函数,即以def f(x): return f(g(x))
的形式编写函数,则很容易将其转换为迭代形成。不幸的是,你通常不会得到一个尾递归调用,所以你需要知道一些技巧。
首先,我们假设我们的功能如下:
def my_map(func, my_list):
if not my_list:
return []
return [func(my_list[0])] + change_my_list(my_list[1:])
好的,所以它是递归的,但不是尾递归的:它真的是
def my_map(func, my_list):
if not my_list:
return []
result = [func(my_list[0])] + change_my_list(my_list[1:])
return result
相反,我们需要稍微调整一下这个函数,添加传统上称为累加器的函数:
def my_map(func, my_list, acc = [])
if not my_list: return acc
acc = acc + func(my_list[0])
return my_map(func, my_list[1:], acc + func(my_list[0]))
现在,我们有一个真正的尾递归函数:我们已经从def f(x): return g(f(x))
转到def f(x): return f(g(x))
现在,将该函数转换为非递归形式非常简单:
def my_map(func, my_list, acc=[]):
while True: #added
if not my_list: return acc
#return my_map(func, my_list[1:], acc + func(my_list[0])) #deleted
func, my_list, acc = func, my_list[1:], acc + func(my_list[0]) #added
现在,我们只是整理一下:
def my_map(func, my_list):
acc = []
while my_list:
acc.append(func(my_list[0])
my_list = my_list[1:]
return acc
请注意,您可以使用for
循环或列表理解进一步清理它,但这仍然是读者的练习。
好的,所以这是一个病态的例子,希望你知道python有一个内置的map
函数,但过程是一样的:转换成一个尾递归形式,用以下命令替换递归调用论证重新分配,并整理。
所以,如果你有:
def make_products(list_of_lists):
if not list_of_lists: return []
first_list = list_of_lists[0]
rest = list_of_lists[1:]
return product_of(first_list, make_products(rest))
您可以将其转换为尾递归形式
def make_products(list_of_lists, acc=[]):
if not list_of_lists: return acc
first_list = list_of_lists[0]
rest = list_of_lists[1:]
acc = product_of(acc, first_list)
return make_products(rest, acc)
然后,这简化为:
def make_products(list_of_lists):
acc=[]
while list_of_lists:
first_list = list_of_lists[0]
rest = list_of_lists[1:]
acc = product_of(acc, first_list)
list_of_lists = rest
return acc
同样,这可以进一步清理,进入for
循环:
def make_products(list_of_lists):
acc=[]
for lst in list_of_lists:
acc = product_of(acc, lst)
return acc
如果您查看了内置函数,您可能会注意到这有点熟悉:它本质上是reduce
函数:
def reduce(function, iterable, initializer):
acc = initializer
for x in iterable:
acc = function(acc, x)
return acc
因此,最终形式类似于
def make_products(list_of_lists):
return reduce(product_of, list_of_lists, []) # the last argument is actually optional here
然后您只需要担心编写product_of
函数。
答案 3 :(得分:0)
如您所知,通常的解决方案是递归。然而,出于无聊,我曾经写过一个java方法multiNext
来做这个没有递归。 multiNext
使用数组来跟踪等效嵌套循环系统中索引的负载。
public static boolean multiNext(int[] current, int[] slotLengths) {
for (int r = current.length - 1; r >= 0; r--) {
if (current[r] < slotLengths[r] - 1) {
current[r]++;
return true;
} else {
current[r] = 0;
}
}
return false;
}
public static void cross(List<List<String>> lists) {
int size = lists.size();
int[] current = new int[size];
int[] slotLengths = new int[size];
for (int i = 0; i < size; i++)
slotLengths[i] = lists.get(i).size();
do {
List<String> temp = new ArrayList<>();
for (int i = 0; i < size; i++)
temp.add(lists.get(i).get(current[i]));
System.out.println(temp);
} while (multiNext(current, slotLengths));
}
public static void main(String[] args) {
cross(Arrays.asList(Arrays.asList("x", "z"), Arrays.asList("a", "b", "c"), Arrays.asList("o", "p")));
}