选择秩算法

时间:2013-07-09 15:58:46

标签: java algorithm

以下是选择等级算法的实现,其用于在阵列中的第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()导致了这个问题,但我不确定。我也不知道如何解决它。

3 个答案:

答案 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

实际上,leftright在这些堆栈帧中是相同的,即算法已停止进展。我们还看到,即使每次选择不同的数据透视索引,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在这种情况下将始终返回rightrank将使用相同的参数调用自身,无限递归。

带回家留言:

  1. 在发现错误时(例如使用调试器和断点)检查程序的状态对于查找错误非常有用。
  2. 不要忽视角落案件。