1

已解决:发布在此评论的末尾。

我不断收到这个错误,我找不到任何解释它为什么会发生。

Exception in thread "main" java.lang.StackOverflowError
at java.util.Random.nextDouble(Random.java:444)
at java.lang.Math.random(Math.java:716)
at assignment6quickSort.M6.qsAlgorithm(M6.java:50)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)

我一直在疯狂搜索,似乎没有人遇到过同样的问题,或者我愚蠢地寻找正确的东西(完全有可能)。

无论如何,我正在创建随机数来为通用快速排序找到一个枢轴数,几个小时前它工作了几次,但现在我每次都收到这个错误。

拜托,我很沮丧……呵呵!我做错了什么?这怎么会导致溢出?

我的代码来了...

package assignment6quickSort;

import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;


public class M6 {

    static M6Comparator<Integer> comp = new M6Comparator<Integer>();
    static Integer[] array = new Integer[20];
    static ArrayList qsSorted = new ArrayList();

    public static void main (String[] args) {       

        for (int i = 0; i < array.length; i++) {
            array[i] = (int)(50 * Math.random());
        }

        for (int i: array) {
            System.out.print(i + ", ");
        }

        quickSort(array, comp);
        System.out.println("\n");

        for (Object i: qsSorted) {
            System.out.print(i + ", ");
        }

    }

    static <T> void quickSort(T[] a, Comparator<? super T> comp) {

        ArrayList<T> temp = new ArrayList<T>();
        for (int i = 0; i < a.length; i++) {
            temp.add(a[i]);
        }

        qsSorted = qsAlgorithm(temp, comp);
    }

    static <T> ArrayList<T> qsAlgorithm(ArrayList<T> a, Comparator<? super T> comp) {

        ArrayList<T> L = new ArrayList<T>();
        ArrayList<T> G = new ArrayList<T>();

        if (a.size() <= 1) 
            return a;
        int pivot = (int)Math.random() * a.size();
        T pivotValue = a.get(pivot);
        for (int i = 0; i < a.size(); i++) {
            if (comp.compare(a.get(i), pivotValue) == -1 || comp.compare(a.get(i), pivotValue) == 0) {
                L.add(a.get(i));
            } else {
                G.add(a.get(i));
            }
        }

        L = qsAlgorithm(L, comp);
        G = qsAlgorithm(G, comp);
        L.addAll(G);

        return L;

    }

}

此外,这是我的比较器:

package assignment6quickSort;

import java.util.Comparator;

public class M6Comparator<E> implements Comparator<E> {

    public int compare(E original, E other) {

        return((Comparable<E>)original).compareTo(other);
    }

}

### 解决方案 ###

显然是一个经典的递归溢出错误。非常感谢@pst 和@Marcin 的帮助!这是 qsAlgorithm() 方法的修订:

static <T> ArrayList<T> qsAlgorithm(ArrayList<T> a, Comparator<? super T> comp) {

        ArrayList<T> L = new ArrayList<T>();
        ArrayList<T> P = new ArrayList<T>();
        ArrayList<T> G = new ArrayList<T>();

        if (a.size() <= 1)
            return a;
        int pivot = (int)Math.random() * a.size();
        T pivotValue = a.get(pivot);
        for (int i = 0; i < a.size(); i++) {
            int v = comp.compare(a.get(i), pivotValue);
            if (v == -1) {
                L.add(a.get(i));
            } else if (v == 0) {
                P.add(a.get(i));
            } else {
                G.add(a.get(i));
            }
        }

        return concatenate(qsAlgorithm(L, comp), P, qsAlgorithm(G, comp));

    }

    static <T> ArrayList<T> concatenate(ArrayList<T> a, ArrayList<T> p, ArrayList<T> b) {

        a.addAll(p);
        a.addAll(b);

        return a;

    }
4

3 回答 3

3

真正溢出堆栈的可能不是 nextDouble 函数,只是碰巧您在每个递归步骤中都调用它,因此它很可能是达到限制的函数。真正的问题是你的递归太深了(也许是你停止递归的错误基本情况)。

看起来a.size是你的变种。确保a确实随着递归的每一步而减少。

于 2013-03-05T18:26:58.170 回答
3

您进入递归调用太多次,直到您的堆栈溢出。问题是您在主比较循环中到达一个点,您总是将所有元素添加到临时数组“L”,而没有添加到“G”,因此您的递归调用:

L = qsAlgorithm(L, comp);

总是使用与参数相同数量的元素:

ArrayList<T> a

所以你永远不会在第 49 行退出递归:

if (a.size() <= 1) 
    return a;

解决方案是在您的临时数组具有最后 2 个相等元素时进行额外的退出。

编辑:一个快速而肮脏的修复......这绝不是有效的代码。我为“偶数”值使用了另一个“E”集合,并将它们添加到最后的结果列表中。

static <T> ArrayList<T> qsAlgorithm(ArrayList<T> a, Comparator<? super T> comp) {

    ArrayList<T> L = new ArrayList<T>();
    ArrayList<T> E = new ArrayList<T>();
    ArrayList<T> G = new ArrayList<T>();

    if (a.size() <= 1) 
        return a;
    int pivot = (int)Math.random() * a.size();
    T pivotValue = a.get(pivot);
    for (int i = 0; i < a.size(); i++) {
        int v = comp.compare(a.get(i), pivotValue);
        if (v == -1) {
            L.add(a.get(i));
        } else if (v == 0) {
            E.add(a.get(i));
        } else {
            G.add(a.get(i));
        }
    }

    L = qsAlgorithm(L, comp);
    G = qsAlgorithm(G, comp);
    L.addAll(E);
    L.addAll(G);

    return L;

}
于 2013-03-05T18:31:27.170 回答
2

Random不是导致堆栈溢出的递归,而是压垮骆驼的最后一根稻草

之前的 2 吨干草捆1

at assignment6quickSort.M6.qsAlgorithm(M6.java:50)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)
at assignment6quickSort.M6.qsAlgorithm(M6.java:60)
..

1提出的代码中的问题是枢轴包含在递归子问题中。想象一下输入的情况,其中两个值都在序列中[x,x]被递归地选择为。将始终是终端(0 个元素),但永远不会是终端(2 个元素)。Lx <= xRL

请参阅Wikipedia 上的 Quicksort并注意伪代码concatenate(quicksort('less'), 'pivot', quicksort('greater'))。也就是说,枢轴元素不包括在子问题中。

于 2013-03-05T18:27:05.057 回答