![](/img/trans.png)
[英]given a set of n integers, return all subsets of k elements that sum to 0
[英]Find all subsets of a set that sum up to n
这是我提出的代码:
static void findNumbers(int[] list, int index, int current, int goal, String result)
{
if (list.length < index || current>goal)
return;
for (int i = index; i < list.length; i++) {
if (current + list[i] == goal) {
System.out.println(result + " " + String.valueOf(list[i]));
}
else if (current + list[i] < goal) {
findNumbers(list, i + 1, current + list[i], goal, result + " " + String.valueOf(list[i]));
}
}
}
用它来调用:
findNumbers(array, starting_index, current_sum_till_now, target_sum, "");
有人可以帮我弄清楚这段代码的时间复杂性,我相信它是指数级的。
解决此问题的最佳方法是什么? 它是否正在使用回溯?
有人指出我犯了一个错误。 当我应该添加它们时,我正在增加递归调用的复杂性。 所以C(N) = C(N-1) + C(N-2) + ...
这同样适用于C(N-1)
, C(N-2)
等。这意味着复杂度不是' O(N!)
。
这让我从另一个角度思考算法。 它正在检查每个可能的子集。 由于存在2^N - 1
可能的子集(不考虑空子集),因此复杂度为O(2^N)
,我认为这是您的原始赌注。
您可以修改您的代码,使其工作原理是“如果数字是好的 - 添加它;忽略任何条件跳过当前数字”。 在这种情况下,代码将是:
static void findNumbers(int[] list, int index, int current, int goal, String result)
{
if (list.length <= index || current>goal) // I've added the "=" which is missing in your code.
return;
if (current + list[index] == goal) {
System.out.println(result + " " + String.valueOf(list[i]));
}
else if (current + list[index] < goal) {
findNumbers(list, index + 1, current + list[i], goal, result + " " + String.valueOf(list[i]));
}
findNumbers(list, index + 1, current, goal, result);
}
在这种情况下,复杂度将是O(2^n)
,这对于n=>5
然后是O(n!)
更好。 正如所指出的,如果对数组进行排序,则复杂性会降低。 这意味着您可以将第二个递归调用放在else if
因为您将确保后面的所有数字都大于当前list[index]
意味着跳过此值没有用,因为此调用的所有后续分支都不会生成任何有效的子集。
在这种情况下,最坏的情况是O(2^l)
,其中l
是一个数字的索引,该数字大于你的目标并且在你的数组中,或者如果这样的数字不存在则为n
。
调用应该是: findNumbers(list,0,0,goal,"")
刚刚指出它比N ^ 2差,实际上它看起来像O(N!)。 您可以保存一些,因为您可以提前退出某些循环,但保存的程度取决于消除可能性的速度。
对于一个更加优化的解决方案,你将要努力,这是递归的一个很好的例子,因为任何基于循环的构造都将是可怕的。 您可以通过预先对数据进行排序来节省一些时间,以便首先获得更大的值,从而更快地达到目标(这将基本上消除列表中立即大于目标的任何内容)。 在消除了太大的条目后,我不确定它是否会直接帮助,因为您仍然需要将所有内容与所有内容进行比较,但它可能会改进处理器分支预测。
这是一种使用动态编程和背包类比的方法: -
按升序对集进行排序
评估子集直到list[i] <= N
解决背包的容量N和物品的价值和重量作为他们的list[i]
如果在最终背包容量N ==最大利润时,则存在至少一个解决方案子集。
使用成本矩阵回溯所有背包解决方案并获取所有解决方案子集。
时间复杂度: O(|S|*N + K) |S|- length of set and K is number of subsets.
这是伪多项式时间算法。
注意:问题是NP-hard没有发现多项式时间算法。
编辑: -从布尔矩阵中回溯解决方案
void retrace(int n,boolean[] solution,int target) {
if(n>=0) {
if(table[target][n-1]) {
solution[n] = false;
retrace(n-1,solution,target);
}
if(table[target-numbers[n]][n-1]) {
solution[n] = true;
retrace(n-1,solution,target-numbers[n]);
}
}
else {
printf("\nsubset:-\n");
for(int i=0;i<solution.length;i++) {
if(solution[i]) {
printf(number[i]+" ");
}
}
}
}
Call : - retrace(numbers.length-1,new boolean[numbers.length],target);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.