返回列表列表的叉积

时间:2016-04-01 18:36:13

标签: algorithm data-structures language-agnostic

给定一个大小为n的列表,编写一个程序,返回每个列表中包含的所有可能的元素组合。

示例:

  • 列出A =" x,z"
  • 列表B =" a,b,c"
  • 列出C =" o,p"

输出:

  • x a o
  • x a p
  • x b o
  • x b p
  • .....
  • z c p

订单并不重要,但困难的部分是:您不能使用递归。 我的解决方案:

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]);
        }
    }
}

正如您所看到的,它只有在我知道手头的列表数量之后才有效。我很好奇,如果递归是解决它的唯一方法。

4 个答案:

答案 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中回答这个问题,因为虽然它目前被标记为,但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")));
}