Java 如何用O(n)空间复杂度构建此树? 问题

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,

给定一组整数,找到这些整数的子集,其总和为100000000

解决方案 我试图构建一棵树,包含给定集合的所有组合以及总和。例如,如果给定的集合看起来像
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))