什么';Java中递归列表的正确方法是什么?

什么';Java中递归列表的正确方法是什么?,java,list,recursion,Java,List,Recursion,在Java中递归列表时,我经常会多次分配和复制列表。例如,我想生成一个包含所有可能整数序列的列表,其中: 每个数字都在1到9之间 每个数字都大于或等于它前面的数字 一个数字可以在一行中出现0到3次 例如,[1,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9]是最大的序列。我有一个递归方法可以做到这一点: static void recurse(List<List<Integer>> addto, List<Integer> pr

在Java中递归列表时,我经常会多次分配和复制列表。例如,我想生成一个包含所有可能整数序列的
列表,其中:

  • 每个数字都在1到9之间
  • 每个数字都大于或等于它前面的数字
  • 一个数字可以在一行中出现0到3次
例如,
[1,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9]
是最大的序列。我有一个递归方法可以做到这一点:

static void recurse(List<List<Integer>> addto, List<Integer> prev, int n){
    if(n>=10)
        addto.add(prev);
    else{
        for(int i=0; i<=3; i++){
            List<Integer> newlist = new ArrayList<Integer>(prev);
            for(int k=0; k<i; k++){
                newlist.add(n);
            }
            recurse(addto, newlist, n+1);
        }
    }
}
static void recurse(列表addto、列表prev、int n){
如果(n>=10)
addto.add(上一页);
否则{

对于(int i=0;i速度减慢可能是由于重新分配了ArrayList内部使用的内存。默认情况下,ArrayList以10的容量开始。当添加第11个元素时,它必须扩展该容量,但仅扩展50%。此外,当使用copy构造函数创建ArrayList时,新列表的容量甚至更小r capacity——源列表中的实际元素数加上我认为的10%(我猜你的10循环算法版本只使用了一个“工作”列表,它在添加到列表之前复制了一个)

因此,您可以尝试在创建列表时提供容量,看看这是否会加快速度:

List<Integer> newlist = new ArrayList<Integer>(27);  // longest list size is 9 * 3
newlist.addAll(prev);
List newlist=newarraylist(27);//最长列表大小为9*3
newlist.addAll(上一个);

编辑:顺便说一句,您应该能够实现无10个嵌套循环的非递归算法。使用堆栈,类似于深度优先树搜索。

速度减慢可能是由于重新分配了ArrayList内部使用的内存。默认情况下,ArrayList的容量为10。添加第11个el时此外,当你用复制构造函数创建一个ArrayList时,新列表的容量会更小——源列表中的实际元素数加上我认为的10%。(我猜你的10个循环版本的算法只使用了一个“工作”项)列表,它在添加到列表之前复制了该列表)

因此,您可以尝试在创建列表时提供容量,看看这是否会加快速度:

List<Integer> newlist = new ArrayList<Integer>(27);  // longest list size is 9 * 3
newlist.addAll(prev);
List newlist=newarraylist(27);//最长列表大小为9*3
newlist.addAll(上一个);

编辑:顺便说一句,您应该能够实现一个没有10个嵌套循环的非递归算法。使用堆栈,类似于深度优先树搜索。

您应该修改列表,而不是复制列表,并且只有在找到解决方案时才复制它。退出递归后,删除最后三个元素名单的最后一部分

static void recurse(List<List<Integer>> addto, List<Integer> list, int n){
        if(n>=10)
                addto.add(new ArrayList<Integer>(list));
        else{
                int pos = list.size();
                for(int i=0; i<=3; i++){
                        list.add(n);
                        recurse(addto, newlist, n+1);
                }
                for(int i=2; i>=0; i--){
                        list.remove(i);
                }
        }
}
static void recurse(List addto,List List,int n){
如果(n>=10)
addto.add(新数组列表(列表));
否则{
int pos=list.size();
对于(int i=0;i=0;i--){
列表。删除(i);
}
}
}

如果您想要更高的性能,请尝试使用int[],而不是整数的ArrayList,因为这样可以节省创建整数的时间。您可以使用27个元素调整列表数组的大小,并传递递归中第一个空闲位置的索引。

而不是复制列表,您应该就地修改列表,并仅在找到解决方案时复制它。退出递归后,删除列表的最后三个元素

static void recurse(List<List<Integer>> addto, List<Integer> list, int n){
        if(n>=10)
                addto.add(new ArrayList<Integer>(list));
        else{
                int pos = list.size();
                for(int i=0; i<=3; i++){
                        list.add(n);
                        recurse(addto, newlist, n+1);
                }
                for(int i=2; i>=0; i--){
                        list.remove(i);
                }
        }
}
static void recurse(List addto,List List,int n){
如果(n>=10)
addto.add(新数组列表(列表));
否则{
int pos=list.size();
对于(int i=0;i=0;i--){
列表。删除(i);
}
}
}

如果您想要更高的性能,请尝试使用int[],而不是整数的ArrayList,因为这样可以节省整数的创建。您可以使用27个元素调整列表数组的大小,并传递递归中第一个自由位置的索引。

正如其他人所指出的,创建整数对象也会带来开销,但这并不能解释非递归算法的原因(可能也使用整数列表)速度更快。实际上,由于整数是不可变的,您可以通过首先构建九个所需整数对象(新整数(1)、新整数(2)等)的列表来避免此问题使用这些,而不是重新创建整数!这可能看起来很奇怪,但实际上会使算法易于推广,适用于任何对象列表,而不仅仅是数字。正如其他人所指出的,也有创建整数对象的开销,但这并不能解释非递归算法的原因(可能也使用整数列表)速度更快。实际上,由于整数是不可变的,您可以通过首先构建九个所需整数对象(新整数(1)、新整数(2)等)的列表来避免此问题使用这些,而不是重新创建整数!这可能看起来很奇怪,但它实际上会使算法易于推广,适用于任何对象列表,而不仅仅是数字。