当我尝试在长度大于50的数组上使用递归排序时,我得到一个StackOverFlowError。如果数组足够小,不会抛出此错误,该方法会正确排序。有没有办法解决这个问题?
public class RecursiveSort
{
static double[] arr;
static int count = 0;
public static void main(String[] args)
{
double[] anArr = new double[50];
for(int i = 0; i < anArr.length; i++) {
anArr[i] = Math.random();
}
arr = anArr;
recurseSort(arr.length - 1);
display();
}
public static void recurseSort(int position)
{
if(position == 0)
{
position = arr.length - 1;
count++;
}
if (arr[position] < arr[position - 1])
{
double n = arr[position - 1];
arr[position - 1] = arr[position];
arr[position] = n;
}
if(count <= arr.length)
recurseSort(--position);
}
public static void display()
{
for(double n : arr)
System.out.println(n);
}
}
答案 0 :(得分:1)
您的递归是尾递归,因此您可以将其替换为do-while
public static void recurseSort(int position)
{
do{
if(position <= 0)
{
position = arr.length - 1;
count++;
}
if (arr[position] < arr[position - 1])
{
double n = arr[position - 1];
arr[position - 1] = arr[position];
arr[position] = n;
}
position--;
}while(count <= arr.length); //end while
}
答案 1 :(得分:0)
首先,您似乎实现了某种bubble sort。我从未尝试使用递归,并且没有看到这样做的任何意义,因为实现此算法通常是直接的并且由两个嵌套循环组成,其内部有条件地交换相邻的列表元素。我没有看到任何递归方法......
毫不奇怪,你在那里做的并不是真正的递归,因为你只对常量维度的静态类成员(arr
,count
)进行操作。然而,排序算法中递归的优点在于每个递归步骤中相应参数复杂度的降低;通过使用一个方法,在将所述列表拆分为两个之后,将未排序的列表作为参数调用自身两次,每次调用该方法时,对这两个子集中的每一个进行排序变得更加容易。这样的方法可能看起来像这样:
private static double[] sort(double[] arr) {
int splt = arr.length >> 2;
// terminate if sorting is trivial
if (splt < 3) {
if (splt < 2) return arr;
if (arr[0] > arr[1]) {
return new double[2]{arr[1], arr[0]};
} else return arr;
}
// split array and sort separately
double[] left = new double[splt];
double[] rght = new double[arr.length-splt];
System.arraycopy(arr, 0, left, 0, splt);
System.arraycopy(arr, splt, rght, 0, rght.length);
return mergeArraysSomehow(sort(left), sort(right));
}
适当的除法可以将递归深度限制为log(n)
,其中n
是数组长度。你正在做的是当它实际上只是继续循环时继续进行方法调用。每次recurseSort
调用自身时,其字节码,参数和私有变量都会被复制到堆栈中,而不会返回任何调用,从而释放堆栈内存!只有当堆栈持有n
的{{1}} 2 调用时,方法调用才能开始返回。值recurseSort
的{{1}}就足够了[因为被平方]来填充并超过整个堆栈大小。
如果必须以递归方式执行此操作,请至少在n
不再需要交换元素时停止递归。或者忘记冒泡排序并实现快速排序或合并排序。
答案 2 :(得分:0)
问题在于递归基于定义两个步骤,一个停止步骤(或基本情况)和一个递归步骤。在后者中,您需要使用递归调用解决整个问题,但问题较小。例如,乘法可以通过将乘数指示的相同数量相加多次来实现(4乘5表示将4加总为5的次数)。如果以递归方式执行,则为:
int multiply(int n, int multiplier)
{
// Stopping step or base case
if (multiplier == 1) return n;
// Recursion step: Solving the whole problem using a recursion call
return n + multiply(n, multiplier - 1);
}
在你的程序中你有基本情况,但在递归步骤中你没有解决整个问题。如果它们应该被交换,你可以交换arr [position]和arr [position-1],但这并不能保证递归调用中的整个子数组都会被排序。如果解决了整个问题,就可以解决递归问题。这就是为什么快速排序算法(和其他人)分裂数组选择一个元素,并将每个小于此的其他元素放在其左侧,并且每个其他更大的元素,最后,递归地调用自身以对两个半部分进行排序。阵列。
当然,这是J. Katzwinkel所描述的问题。