在Java中使用对象进行递归的有效方法?

在Java中使用对象进行递归的有效方法?,java,recursion,Java,Recursion,在使用Java的动态编程中,我经常希望在递归步骤中将数组或对象作为参数传递。但是,为了取消修改原始值,我必须在将对象作为参数传入之前克隆它;如果这样做,我会有一个非常不利的时间和空间复杂性,因为该方法可能有数千个堆栈。我从中了解到,解决这个问题的一个可能方法是在完成计算后撤消该过程。但是,有时这是不可能撤消的(例如,当递归方法从数组中删除元素时)。有人能解决这个问题吗?我在竞争性编程的环境中使用它,有时自下而上的解决方案过于复杂和不切实际 例如: 公共静态int方法(ArrayList列表){

在使用Java的动态编程中,我经常希望在递归步骤中将数组或对象作为参数传递。但是,为了取消修改原始值,我必须在将对象作为参数传入之前克隆它;如果这样做,我会有一个非常不利的时间和空间复杂性,因为该方法可能有数千个堆栈。我从中了解到,解决这个问题的一个可能方法是在完成计算后撤消该过程。但是,有时这是不可能撤消的(例如,当递归方法从数组中删除元素时)。有人能解决这个问题吗?我在竞争性编程的环境中使用它,有时自下而上的解决方案过于复杂和不切实际

例如:

公共静态int方法(ArrayList列表){
if(list.size()==1){
返回列表。获取(0);
}
ArrayList clone=新的ArrayList();
用于(int e:列表){
克隆。添加(e);
}
clone.remove(clone.size()-1);
方法(克隆);
}
如果我没有克隆它,而是这样做:

公共静态int方法(ArrayList列表){
if(list.size()==1){
返回列表。获取(0);
}
list.remove(list.size()-1);
方法(列表);
}

原始对象将被修改。在这种情况下,显然可以返回数组的第一个元素,而不是创建这个cuber-stone代码。然而,我的观点是要说明传递对象的问题。

对于一个如此普遍的问题(以及理解普遍性的基本原理),答案是存在的。这些是在Haskell这样的语言中用来进行高效计算的东西,在这些语言中,一切都是不可变的,所有算法都基于递归,因此它们当然适合您的情况。这就是说,Java并没有针对此类操作进行优化,因此您可能会发现,在实践中,听起来更笨拙的解决方案编码和/或执行起来更快。

对于如此普遍的问题(并理解其普遍性的基本原理),答案是肯定的。这些是在Haskell这样的语言中用来进行高效计算的东西,在这些语言中,一切都是不可变的,所有算法都基于递归,因此它们当然适合您的情况。也就是说,Java并没有针对此类操作进行优化,因此您可能会发现,在实践中,听起来更笨拙的解决方案编码和/或执行起来更快。

如果您需要与以前一样的列表或数组,那么您可以在递归修改复制的版本之前复制它一次,但是您不需要在每个递归步骤都复制它。

如果您需要与以前一样的列表或数组,那么您可以在递归修改复制的版本之前复制它一次,但不需要在每个递归步骤都复制它。

使用不可变的数据结构。有多个可用的库。若要扩展@thatotherguy的注释,要么需要对对象进行变异,要么不进行变异。如果不是,则使其不可变。传递对象引用已经变得非常“高效”,并且没有理由克隆对象,因为您可以使用其他机制来防止无意中的修改。您能否具体说明,你遇到了什么类型的递归问题,而你实际上需要一个真正的副本?@JimGarrison我之所以需要克隆对象,是因为当我们将一个对象传递给一个方法时,该对象没有被复制,而是它的内存位置被复制了。因此,如果该方法修改了对象,则原始对象也将被修改。@在竞争性编程中,其他人不会将库带入竞赛,创建不可变的数组/对象将浪费时间。请使用不可变的数据结构。有多个可用的库。若要扩展@thatotherguy的注释,要么需要对对象进行变异,要么不进行变异。如果不是,则使其不可变。传递对象引用已经变得非常“高效”,并且没有理由克隆对象,因为您可以使用其他机制来防止无意中的修改。您能否具体说明,你遇到了什么类型的递归问题,而你实际上需要一个真正的副本?@JimGarrison我之所以需要克隆对象,是因为当我们将一个对象传递给一个方法时,该对象没有被复制,而是它的内存位置被复制了。因此,如果该方法修改了对象,则原始对象也将被修改。@在竞争性编程中,其他人不会将库带入竞赛,创建不可变数组/对象将是浪费时间。