Java 如何用O(n)空间复杂度构建此树? 问题
给定一组整数,找到这些整数的子集,其总和为100000000 解决方案 我试图构建一棵树,包含给定集合的所有组合以及总和。例如,如果给定的集合看起来像Java 如何用O(n)空间复杂度构建此树? 问题,java,algorithm,tree,space-complexity,subset-sum,Java,Algorithm,Tree,Space Complexity,Subset Sum,给定一组整数,找到这些整数的子集,其总和为100000000 解决方案 我试图构建一棵树,包含给定集合的所有组合以及总和。例如,如果给定的集合看起来像0,1,2,我将构建以下树,检查每个节点的总和: {} {} {0} {} {1} {0} {0,1} {} {2} {1} {1,2} {0} {2} {0,1} {0,1,
0,1,2
,我将构建以下树,检查每个节点的总和:
{}
{} {0}
{} {1} {0} {0,1}
{} {2} {1} {1,2} {0} {2} {0,1} {0,1,2}
由于我在每个节点保留整数数组和总和,我应该只需要内存中树的最底层(当前)级别。
问题
我当前的实现将在内存中维护整个树,因此占用了太多的堆空间
如何更改当前的实现,以便GC能够处理我的上层树级别
(当我找到目标和时,我正在抛出一个RuntimeException,但这显然只是为了玩游戏)
公共类递归求解器{
静态最终整数目标=100000000;
静态最终整数[]集=新整数[]{98374328、23424123、2341234、123412344等};
树initTree(){
返回nextLevel(新树(null),0);
}
Tree nextLevel(Tree currentLocation,int current){
if(current==set.length){returnnull;}
else if(currentLocation.sum==target)抛出新的RuntimeException(currentLocation.getText());
否则{
currentLocation.left=nextLevel(currentLocation.copy(),current+1);
Tree right=currentLocation.copy();
right.value=add(currentLocation.value,set[current]);
right.sum=currentLocation.sum+set[current];
currentLocation.right=nextLevel(右,当前+1);
返回当前位置;
}
}
int[]添加(int[]数组,int位){
if(数组==null){
返回新的int[]{digit};
}
int[]newValue=newint[array.length+1];
for(int i=0;i
在仔细考虑了erip的评论之后,我意识到他是正确的——我不应该用树来实现这个算法
蛮力通常是O(n*2^n)
,因为2^n
子集中有n
添加项。因为我每个节点只做一次加法,所以我提出的解决方案是O(2^n)
,其中n是给定集合的大小。而且,该算法只具有O(n)
空间复杂度。因为在我的特殊问题中,原始集合中的元素数量很小(大约25)O(2^n)
复杂性不是太大的问题
这个问题的动态解决方案是O(t*n)
,其中t
是目标和,n
是元素数。因为t
在我的问题中非常大,所以动态解决方案的运行时间非常长,内存使用率很高
这在我的机器上大约311毫秒就完成了我的特定解决方案,这比我看到的针对这类问题的动态编程解决方案有了巨大的改进
public class TailRecursiveSolver {
public static void main(String[] args) {
final long starttime = System.currentTimeMillis();
try {
step(new Subset(null, 0), 0);
}
catch (RuntimeException ex) {
System.out.println(ex.getMessage());
final long endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
}
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
static void step(Subset current, int counter) {
if (current.sum == target) throw new RuntimeException(current.getText());
else if (counter == set.length) {}
else {
step(new Subset(add(current.subset, set[counter]), current.sum + set[counter]), counter + 1);
step(current, counter + 1);
}
}
static int[] add(int[] array, int digit) {
if (array == null) {
return new int[]{digit};
}
int[] newValue = new int[array.length + 1];
for (int i = 0; i < array.length; i++) {
newValue[i] = array[i];
}
newValue[array.length] = digit;
return newValue;
}
}
class Subset {
int[] subset;
int sum;
Subset(int[] subset, int sum) {
this.subset = subset;
this.sum = sum;
}
public String getText() {
String ret = "";
for (int i = 0; i < (subset == null ? 0 : subset.length); i++) {
ret += " + " + subset[i];
}
if (ret.startsWith(" ")) {
ret = ret.substring(3);
ret = ret + " = " + sum;
} else ret = "null";
return ret;
}
}
编辑2-
这里是另一种使用中间相遇攻击的版本,同时还有一点移位,以减少从<代码> O(2 ^ n)< /代码>到<代码> O(2 ^(n/2))< /> > >
的复杂性。 如果要对32到64个元素之间的集合使用此选项,则应将表示步长函数中当前子集的int
更改为long
,尽管随着集合大小的增加,性能会明显降低。如果要将其用于元素数为奇数的集合,应向集合中添加0以使其成为偶数
import java.util.ArrayList;
import java.util.List;
public class SubsetSumMiddleAttack {
static final int target = 100000000;
static final int[] set = new int[]{ ... };
static List<Subset> evens = new ArrayList<>();
static List<Subset> odds = new ArrayList<>();
static int[][] split(int[] superSet) {
int[][] ret = new int[2][superSet.length / 2];
for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i];
return ret;
}
static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) {
accumulator.add(new Subset(subset, sum));
if (counter != superSet.length) {
step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1);
step(superSet, accumulator, subset, sum, counter + 1);
}
}
static void printSubset(Subset e, Subset o) {
String ret = "";
for (int i = 0; i < 32; i++) {
if (i % 2 == 0) {
if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
else {
if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
}
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum);
System.out.println(ret);
}
public static void main(String[] args) {
int[][] superSets = split(set);
step(superSets[0], evens, 0,0,0);
step(superSets[1], odds, 0,0,0);
for (Subset e : evens) {
for (Subset o : odds) {
if (e.sum + o.sum == target) printSubset(e, o);
}
}
}
}
class Subset {
int subset;
int sum;
Subset(int subset, int sum) {
this.subset = subset;
this.sum = sum;
}
}
import java.util.ArrayList;
导入java.util.List;
公共类SubsetSummidLeattack{
静态最终整数目标=100000000;
静态最终整数[]集=新整数[]{…};
静态列表evens=newarraylist();
静态列表赔率=新的ArrayList();
静态int[]split(int[]superSet){
int[]ret=新int[2][superSet.length/2];
对于(inti=0;i(i/2)))==1)ret+=“+”+集[i];
}
否则{
如果((1&(o.subset>>(i/2)))==1)ret+=“+”+set[i];
}
}
如果(ret.startsWith(“”)ret=ret.substring(3)+“=”+(e.sum+o.sum);
系统输出打印项次(ret);
}
公共静态void main(字符串[]args){
int[][]超集=拆分(集);
阶跃(超集[0],偶,0,0,0);
步长(超集[1],赔率,0,0,0);
用于(子集e:偶数){
对于(子集o:赔率){
如果(e.sum+o.sum==目标)打印子集(e,o);
}
}
}
}
类子集{
整数子集;
整数和;
子集(整数子集,整数和){
this.subset=子集;
this.sum=sum;
}
}
问题是
如果您真的想提高性能,那么就必须忘记树实现。您要么只需生成所有子集并求和,要么使用dyna
public class SubsetSumSolver {
static boolean found = false;
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
public static void main(String[] args) {
step(0,0,0);
}
static void step(long subset, int sum, int counter) {
if (sum == target) {
found = true;
System.out.println(getText(subset, sum));
}
else if (!found && counter != set.length) {
step(subset + (1 << counter), sum + set[counter], counter + 1);
step(subset, sum, counter + 1);
}
}
static String getText(long subset, int sum) {
String ret = "";
for (int i = 0; i < 64; i++) if((1 & (subset >> i)) == 1) ret += " + " + set[i];
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + sum;
else ret = "null";
return ret;
}
}
import java.util.ArrayList;
import java.util.List;
public class SubsetSumMiddleAttack {
static final int target = 100000000;
static final int[] set = new int[]{ ... };
static List<Subset> evens = new ArrayList<>();
static List<Subset> odds = new ArrayList<>();
static int[][] split(int[] superSet) {
int[][] ret = new int[2][superSet.length / 2];
for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i];
return ret;
}
static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) {
accumulator.add(new Subset(subset, sum));
if (counter != superSet.length) {
step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1);
step(superSet, accumulator, subset, sum, counter + 1);
}
}
static void printSubset(Subset e, Subset o) {
String ret = "";
for (int i = 0; i < 32; i++) {
if (i % 2 == 0) {
if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
else {
if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
}
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum);
System.out.println(ret);
}
public static void main(String[] args) {
int[][] superSets = split(set);
step(superSets[0], evens, 0,0,0);
step(superSets[1], odds, 0,0,0);
for (Subset e : evens) {
for (Subset o : odds) {
if (e.sum + o.sum == target) printSubset(e, o);
}
}
}
}
class Subset {
int subset;
int sum;
Subset(int subset, int sum) {
this.subset = subset;
this.sum = sum;
}
}
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))