Java查找数组中所有组合的加起来为特定数字的组合

时间:2016-08-21 02:37:02

标签: java algorithm combinatorics

我目前正在从一本采访书中处理以下问题:

  

您将获得一个包含50个唯一整数的随机数组,范围从1到100(包括1和100)。使用Java编写一个方法,该方法接受一个正整数作为参数,并返回一个包含所有数字组合的数组,这些数字组合加起来就是该值。

例如,给定一个整数数组[3,6,1,9,2,5,12]并传递整数值9,你会返回[[3,6],[6,1,2] ],[9],[3,1,5]。在数组中返回结果的顺序无关紧要,尽管你应该返回唯一的集合(即[6,3]和[3,6]是相同的,只返回一个)。此外,个别结果应该按照它们的顺序排列(即[6​​,1,2]应该返回,而不是[1,2,6])。

我已经取得了不错的进展,但我担心我可能会以错误的方式解决这个问题。

import java.util.*;


public class findCombinations {
  public static void main(String[] args) {
    int number;
    int[] list = new int[10];
    Scanner reader = new Scanner(System.in);

    //fill the array
    for (int i = 0; i < list.length; i++) {
      number = (int)(Math.random() * 10) + 1;
      list[i] = number;
      for (int j = 0; j < i; j++) { //remove duplicates
        if (list[i] == list[j]) {
          i--;
          break;
        }
      }
    }

    Arrays.sort(list);

    //test output
    for (int i = 0; i < list.length; i++) {
      System.out.println(list[i]);
    }
    System.out.println("Enter a number: ");
    int input = reader.nextInt(); 
    ArrayList<Integer> trimmedList = new ArrayList<Integer>();

    //cut out the numbers that are impossible to use
    for (int i = 0; i < list.length; i++) {
      if (list[i] <= input) {
        trimmedList.add(list[i]);
      }
    }
    //test output
    printList(trimmedList);

    ArrayList<Integer> comboList = new ArrayList<Integer>();
    System.out.println("Finding combinations...");

    for (int i = 0; i < trimmedList.size(); i++) {
      int current = trimmedList.get(i);
      if (current == input) { System.out.println(current); }
      else if (current < input) {
        comboList.add(current);
        if (isCombo(comboList, input)) {
          printList(comboList);
        }
        else { continue; }
      }
      else { continue; }
    }
  }

  public static boolean isCombo(ArrayList<Integer> list, int input) {
    ArrayList<Integer> combo = new ArrayList<Integer>();
    int sum = 0;

    for (int i : list)
      sum += i;

    if (sum == input) { return true; }
    else { return false; }
  }

  public static void printList(ArrayList<Integer> list) {
    for (int i = 0; i < list.size(); i++) {
      System.out.print(list.get(i));
    }
  }
 }

我知道这是不完整的但是我想问一下是否有人对此有任何建议或改进?我对列表进行了排序并删除了所有可能无法使用的整数,但现在困难的部分是找到所有组合。

3 个答案:

答案 0 :(得分:1)

由于这是一项学习练习,如果你能自己解决这个问题,你将受益最多。所以...

提示:

  1. 首先对数字进行排序是在正确的轨道上
  2. 我会使用递归来迭代解决方案。给定一个部分总和,只有少于一定数量的数字才有可能被添加到总和......
  3. 在&lt;之前&gt;计算出你头脑中的算法&gt;你开始编码了。
  4. 我同意@nbrooks关于采访者正在寻找什么的话题。你需要能够思考......并在算法层面向面试官解释你的想法。这就是将优秀候选人与普通候选人区分开来的原因。

答案 1 :(得分:1)

有许多不同的方法可以解决这个问题,每个方法都有自己的优点,所以我不会过分担心你的答案是否是正确的。一个或不......只要它实际上解决了问题!此外,面试官可能会对您的思维过程和您使用的策略更感兴趣,而不是在几分钟内在白板上写出100%完美的解决方案。

以下是需要考虑的几件事:

  • 正如您所注意到的,您可以立即消除任何大于目标值的整数。

  • 您实际上生成了起始数组的任意大小的子集 - 因此Set可能是最有用的数据类型。当您生成响应集时,{2, 3}{3, 2}应视为相同。

  • 整数分区是NP-Complete问题。这很难。我认为您采用了正确的方法来开始使用数组,而不是使用目标值。

  • 有许多算法可用于生成较大集合的整数组合。看看这个SO answer中的一些。您可以从(已经过滤的)起始集生成k大小的组合,{1- 1}来自1-50。

  • 实际上......有更多直接的方式来获得你的起始集power set。考虑一组电源的固有结构(如下所示)。通过列举几个示例,您会发现策略中识别子集的情况会自然复发。

当您正在生成这些组合时,请丢弃任何其元素不会与您的目标值相加的内容。

https://en.wikipedia.org/wiki/Power_set

图片来源:https://en.wikipedia.org/wiki/Power_set

答案 2 :(得分:0)

我意识到生成随机数的数组不是问题陈述的一部分,但我认为你的困难从这里开始。

首先,使用Set<Integer>类型集合来收集生成的数字;当集合达到所需大小时中断。如果生成的顺序很重要,请使用LinkedHashSet。

Set<Integer> origSet = new HashSet<Integer>(); // fill with random numbers

在某些时候,您有一个订单重要的数字列表。将此列表维护为List<Integer>。该列表保留了原始列表的顺序,以便您可以按正确的顺序生成数字组合(即,6先于1,1先于2)。

List<Integer> origList = new ArrayList<Integer>(origSet); // use indexOf method to find index of a number

您创建第二个已排序的列表;此列表是递归算法使用的列表。

List<Integer> sortedList = new ArrayList<Integer>(origList); // sort this

您不需要修剪列表,因为递归算法会修剪任何分支而没有可行的解决方案。

递归算法可以用更少的代码行生成组合。重新排序需要更多行。