【问题标题】:All Possible k combinations of list with size n大小为 n 的所有可能的 k 个列表组合
【发布时间】:2020-07-16 16:21:45
【问题描述】:

我正在尝试从大小为 N 的列表中获取大小为 K 的所有可能组合。

我有一个带有“人类”对象的列表,我正在尝试创建一个新的 ArrayList,它将用列表对象填充。每个列表都是“人类”对象的不同组合。

一个简单的数字示例是:从一个包含 1,2,3 的列表中,我想得到一个 ArrayList,它看起来像这样:[[1,2], [1,3], [2,3]] 我也不介意它看起来像这样:[[1], [2], [3], [1,2], [1,3], [2,3]]

这是我的代码:

public void combination(List<Human> people, int k, ArrayList<List> result) {
    if (people.size() < k) {
        return;
    }
    if (k == 1) {
        for (Human hum : people) {
            List<Human> combinations = new ArrayList<Human>();
            combinations.add(hum);
            result.add(combinations);
        }
    }
    else if (people.size() == k) {
        List<Human> combinations = new ArrayList<Human>();
        for (Human hum : people) {
            combinations.add(hum);
        }
       result.add(combinations);
    }
    else if (people.size() > k) {
        for (int i = 0; i < people.size(); i++) {
            List<Human> combinations = new ArrayList<Human>();
            combinations.add(people.get(i));
            result.add(combinations);
            combination(people.subList(i + 1, people.size()), k - 1, result);
        }
    }
}

我使用本站最后一种方法作为参考:https://hmkcode.com/calculate-find-all-possible-combinations-of-an-array-using-java/

目前我在新的 ArrayList 中获得了正确数量的结果,但里面的每个 List 只包含一个 Human。

我高度怀疑问题出在最后一个else if,因为我很难理解递归。

请随时提出任何问题或为此提出任何其他实施建议。

【问题讨论】:

  • 除了 else if 之外的所有内容都是快速跳过最后一个 for 循环的代码。在最后一个for 循环中,为每个人创建了一个组合,并且只添加了一个人,所以当然每个组合中只有一个人。我认为列表中的每个人都是独一无二的?
  • @MaartenBodewes 感谢您的回复,这就是我的想法,但是我无法弄清楚如何修改“结果”以获得我需要的东西
  • 也许你错过了我评论中的问题?列表中没有骗子?
  • @MaartenBodewes 是的,抱歉,列表中的每个人都是独一无二的,是的,没有骗子
  • 这不是已经在最后一个循环中发生了吗,我用较小的“k”和较小的人员列表递归调用它?

标签: java arrays list combinations


【解决方案1】:

问题出在您的循环中,您仅在连续子列表上调用 combination 函数(例如,如果初始集合是 [1,2,3,4,5],则您没有在[1,3,5])。

另外,请记住,您应该在 Human 类中覆盖 equals 函数。

    private void subsetsOf(List<Human> humans, int k, int index, Set<Human> tempSet, List<Set<Human>> finalSet) {
    if (tempSet.size() == k) {
        finalSet.add(new HashSet<>(tempSet));
        return;
    }

    if (index == humans.size())
        return;


    Human human = humans.get(index);

    tempSet.add(human);
    subsetsOf(humans, k, index+1, tempSet, finalSet);

    tempSet.remove(human);
    subsetsOf(humans, k, index+1, tempSet, finalSet);
}

public List<Set<Human>> combination(List<Human> humans, int k) {
    List<Set<Human>> result = new ArrayList<>();
    subsetsOf(humans, k, 0, new HashSet<Human>(), result);
    return result;
}

【讨论】:

  • 非常优雅的解决方案,我想应该也是最佳的。
【解决方案2】:

这是另一个实现,它从不删除任何东西,也不会创建任何东西的虚假列表。它只在需要时添加,T 不需要 equals,只要它们本身是唯一的。

只是为了学习经验,就在我的脑海中:

package com.stackexchange.so;

import java.util.ArrayList;
import java.util.List;

public class RecursiveCombinationCreator {
    /**
     * Creates combinations of elements of a specific combination size.
     * 
     * @param <T>             the type of the elements
     * @param elts            a list of elements, which should be unique
     * @param combinationSize the size of the combinations
     * @return a list of combinations
     */
    public static <T> List<List<T>> createCombinations(List<T> elts, int combinationSize) {
        var fullCombinations = new ArrayList<List<T>>();
        createCombinations(elts, fullCombinations, new ArrayList<T>(), 0, combinationSize);
        return fullCombinations;
    }

    /**
     * Recursive function that grows the combination size, and adds full combinations when the combination size is met.
     * To avoid duplicates, only elements that are higher in the list are added to the combination. The combination is
     * complete when <code>missing == 0</code>.
     * 
     * @param <T>              the type of the elements
     * @param elts             the elements to create combinations from, all elements should be unique
     * @param fullCombinations the final result array of the combinations, shared among all recursive calls
     * @param combination      the current combination that needs to get <code>missing<code> members
     * @param index            the index of the element one higher than the last element in the combination
     * @param missing          the amount of elements needed to complete the combination
     */
    private static <T> void createCombinations(List<T> elts, List<List<T>> fullCombinations, List<T> combination,
            int index, int missing) {
        if (missing == 0) {
            fullCombinations.add(combination);
            return;
        }

        // we don't need to go over elts.size() - missing because then the combination cannot be completed, too few left
        for (int i = index; i <= elts.size() - missing; i++) {
            List<T> newCombination;
            if (i == elts.size() - missing) {
                // optimization: avoid dereferencing the final combination, reuse
                newCombination = combination;
            } else {
                newCombination = new ArrayList<T>(combination);
            }
            newCombination.add(elts.get(i));
            createCombinations(elts, fullCombinations, newCombination, i + 1, missing - 1);
        }
    }

    // === TEST CODE ===

    // we needed humans, OK
    private static class Human {
        private int id;

        public Human(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return Integer.toString(id);
        }
    }

    public static void main(String[] args) {
        // generate input
        var humans = new ArrayList<Human>();
        for (int id = 0; id < 200; id++) {
            var human = new Human(id);
            humans.add(human);
        }

        // just that one call
        var fullcombinations = createCombinations(humans, 199);

        // show output
        System.out.println(fullcombinations.size());
        for (List<Human> combination : fullcombinations) {
            System.out.println(combination);
        }
    }
}

还要注意,最大深度等于k(或k + 1),这意味着您的堆栈可以避免过多的递归调用。

【讨论】:

  • 我猜应该也可以使用有序集,但这需要重新考虑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
相关资源
最近更新 更多