以下是选择等级算法的实现,其用于在阵列中的第K个最小元素之前找到元素。有时这个程序运行良好,但有时由于堆栈溢出错误(代码片段下面的错误)
而失败public static int rand(int left, int right){
return left+(int)(Math.random()*(double)(right-left));
}
public static int rank(int[] array, int left, int right, int rank){
int pivot = rand(left, right);
int leftend = partition(array, left, right, array[pivot]);
int size = leftend - left +1;
if(size == rank+1){
for(int i=0; i<=leftend; i++)
System.out.print(array[i]+" ,");
return 0;
}else if(size > rank)
return rank(array, left, leftend, rank);
else return rank(array, leftend+1, right, rank - size);
}
public static int partition(int[] array, int left, int right, int pivot){
while(true){
while(left <= right && array[left] <= pivot)
left++;
while(right >= left && array[right] > pivot)
right--;
if(left > right)
return left-1;
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
错误:
Exception in thread "main" java.lang.StackOverflowError
at java.util.Random.nextDouble(Random.java:409)
at java.lang.Math.random(Math.java:712)
at mod.rand(mod.java:12)
at mod.rank(mod.java:16)
at mod.rank(mod.java:25)
我想也许rand()导致了这个问题,但我不确定。我也不知道如何解决它。
答案 0 :(得分:2)
我假设您希望rand
方法返回左(含)和右(含)之间的数字。但是,Math.random()方法返回介于0.0(包括)和1.0(不包括)之间的数字。因此,在所评估的子阵列的大小为2的情况下(即:left = 4且right = 5),rand
函数将仅能够返回4。
尝试在rand
函数中更改此行以确保它可以包含上限:
return left+(int)(Math.random()*(double)(right-left));
代表
return left+(int)(Math.random()*(double)(right-left + 1));
答案 1 :(得分:1)
递归从不结束不是因为输入而是因为随机的使用, 理论上随机可以每次给你相同的数字。 例如,更改为:
public static int rand(int left, int right)
{
return right - 1;
}
尝试输入:
int[] array= {1,2,11,67,35};
rank(array, 0, 4, 2);
并且您将在此输入上获得java.lang.StackOverflowError
,因为递归永远不会结束。
答案 2 :(得分:1)
StackOverflowError的Javadoc说:
当由于应用程序过于冗长而发生堆栈溢出时抛出。
由于默认限制非常高,因此StackOverflowError通常表示无限递归。
要有效地诊断此类错误,请使用具有调试支持的体面IDE,为StackOverflowError
设置异常断点,运行程序,并在命中断点时检查堆栈中的局部变量。
这样做,我们得到以下堆栈:
Random.nextDouble() line: 444 [local variables unavailable]
Math.random() line: 716
Test.rand(int, int) line: 5
Test.rank(int[], int, int, int) line: 9
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
Test.rank(int[], int, int, int) line: 18
...
排名似乎从第18行无限制地调用自己。让我们通过检查局部变量来验证这一点。对于第18行的最顶层堆栈帧:
left 97
right 107
rank 5
pivot 102
leftend 107
size 11
下面的那个:
left 97
right 107
rank 5
pivot 101
leftend 107
size 11
下面那个
left 97
right 107
rank 5
pivot 105
leftend 107
size 11
实际上,left
和right
在这些堆栈帧中是相同的,即算法已停止进展。我们还看到,即使每次选择不同的数据透视索引,leftend
仍然等于right
。
查看有问题的数组索引,我们看到:
[97] 10
[98] 10
[99] 10
[100] 10
[101] 10
[102] 10
[103] 10
[104] 10
[105] 10
[106] 10
[107] 10
这看起来好像你没有处理子范围中所有元素都正确相等的特殊情况。
事实上,在查看代码时,我们看到partition
在这种情况下将始终返回right
,rank
将使用相同的参数调用自身,无限递归。
带回家留言: