Java中的堆栈问题

时间:2011-06-05 01:42:47

标签: java eclipse stack stack-overflow quicksort

我在java中遇到堆栈问题...我正在使用ArrayList实现quicksort-我会在最后附上我的完整代码但是这里是相关的部分(请记住我一直在调试了几个小时的地狱,完全不知道出了什么问题,所以你看到以奇/怪方式完成的事情很可能是因为我试图检查那里的错误):

显然执行的呼叫次数过多:

            QuickSort(numArray, m+1, right);

它在这里失败了:

//Swap pivot and left
        numArray.set(pivot, leftVal);

我收到了输出:

Starting sort number: [1] : RandomPivot [ON] : Sorting [RANDOM]
Sorted: 2496 elements.
Took: 53 milliseconds.

Starting sort number: [2] : RandomPivot [ON] : Sorting [RANDOM]
Sorted: 4988 elements.
Took: 25 milliseconds.

Starting sort number: [3] : RandomPivot [ON] : Sorting [RANDOM]
Sorted: 7478 elements.
Took: 49 milliseconds.

Starting sort number: [4] : RandomPivot [ON] : Sorting [RANDOM]
Sorted: 9953 elements.
Took: 85 milliseconds.

Starting sort number: [5] : RandomPivot [ON] : Sorting [RANDOM]
Sorted: 12416 elements.
Took: 131 milliseconds.

Starting sort number: [1] : RandomPivot [ON] : Sorting [SORTED]
Sorted: 2497 elements.
Took: 1 milliseconds.

Starting sort number: [2] : RandomPivot [ON] : Sorting [SORTED]
Sorted: 4984 elements.
Took: 1 milliseconds.

Starting sort number: [3] : RandomPivot [ON] : Sorting [SORTED]
Sorted: 7482 elements.
Took: 2 milliseconds.

Starting sort number: [4] : RandomPivot [ON] : Sorting [SORTED]
Sorted: 9950 elements.
Took: 2 milliseconds.

Starting sort number: [5] : RandomPivot [ON] : Sorting [SORTED]
Sorted: 12424 elements.
Took: 2 milliseconds.

Starting sort number: [1] : RandomPivot [ON] : Sorting [REVERSE SORTED]
Sorted: 2494 elements.
Took: 2 milliseconds.

Starting sort number: [2] : RandomPivot [ON] : Sorting [REVERSE SORTED]
Sorted: 4988 elements.
Took: 10 milliseconds.

Starting sort number: [3] : RandomPivot [ON] : Sorting [REVERSE SORTED]
Sorted: 7470 elements.
Took: 35 milliseconds.

Starting sort number: [4] : RandomPivot [ON] : Sorting [REVERSE SORTED]
Sorted: 9962 elements.
Took: 50 milliseconds.

Starting sort number: [5] : RandomPivot [ON] : Sorting [REVERSE SORTED]
Sorted: 12419 elements.
Took: 65 milliseconds.

Starting sort number: [1] : RandomPivot [OFF] : Sorting [RANDOM]
Sorted: 2497 elements.
Took: 5 milliseconds.

Starting sort number: [2] : RandomPivot [OFF] : Sorting [RANDOM]
Sorted: 4984 elements.
Took: 54 milliseconds.

Starting sort number: [3] : RandomPivot [OFF] : Sorting [RANDOM]
Sorted: 7473 elements.
Took: 47 milliseconds.

Starting sort number: [4] : RandomPivot [OFF] : Sorting [RANDOM]
Sorted: 9958 elements.
Took: 80 milliseconds.

Starting sort number: [5] : RandomPivot [OFF] : Sorting [RANDOM]
Sorted: 12419 elements.
Took: 130 milliseconds.

Starting sort number: [1] : RandomPivot [OFF] : Sorting [SORTED]
Sorted: 2498 elements.
Took: 11 milliseconds.

Starting sort number: [2] : RandomPivot [OFF] : Sorting [SORTED]
Sorted: 4991 elements.
Took: 44 milliseconds.

Starting sort number: [3] : RandomPivot [OFF] : Sorting [SORTED]
Sorted: 7474 elements.
Took: 97 milliseconds.

Starting sort number: [4] : RandomPivot [OFF] : Sorting [SORTED]
Exception in thread "main" java.lang.StackOverflowError
at java.util.ArrayList.set(Unknown Source)
at Sorter.QuickSort(Sorter.java:64)
at Sorter.QuickSort(Sorter.java:107)
at Sorter.QuickSort(Sorter.java:107)
at Sorter.QuickSort(Sorter.java:107)
at Sorter.QuickSort(Sorter.java:107)
    (on and on and on and on)

测试,一旦我超过~7500作为我的ArrayList的大小,它总是失败。总是在“ArrayList.set()”失败,我没有丝毫的线索为什么。正如你所看到的 - 所有其他排序类型的数量都超过了这个数量,但是对于排序列表,我将不得不调用“m + 1,右”n次,其中n是列表的大小。

我试过这个:

    if(m-1>left && m-1<right)
        QuickSort(numArray, left, m-1);
    if(m+1<right && m+1>left)
        QuickSort(numArray, m+1, right);

但是我得到了同样的错误,我觉得它更容易理解,如果它被遗漏了。

如果我可以增加堆栈大小,不知何故,我可以推迟错误,这将允许我至少分析不同的方法。

我正在通过eclipse运行此代码,如果这有任何区别的话。谢谢! (现在完整代码)

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;


public class Sorter {

//STATE:
public boolean test=false;
public boolean randomPivot=false;
Random r = new Random();
public int sortMethod=1;

public static int RANDOM=1;
public static int SORTED=2;
public static int REVERSE_SORTED=3;


public Sorter(){ }


public ArrayList<Integer> SlowSort(ArrayList<Integer> numArray){

    //Add "infinity" to the end
    numArray.add(Integer.MAX_VALUE);


    //TIME AND RUN QUICKSORT
    long startTime = System.currentTimeMillis();
    numArray=QuickSort(numArray, 0, numArray.size()-2);
    long stopTime = System.currentTimeMillis();
    long runTime = stopTime - startTime;

    //Remove "infinity" from the end
    numArray.remove(numArray.size()-1);

    //TODO: Printing effs it up? idk
    //      printArrayWithMessage("Sort Finished.", numArray);

    //PRINT COMPLETION MESSAGE AND RETURN
    System.out.println("Sorted: "+numArray.size()+" elements.\nTook: " + runTime+" milliseconds.\n");
    return numArray;
}


public ArrayList<Integer> QuickSort(ArrayList<Integer> numArray, int left, int right){
    if(left>=right){
        return numArray;
    }

    //Correctly Initialize Pivot
    int pivot=0;
    if(randomPivot){
        pivot = r.nextInt(right-left);
    }
    pivot+=left;

    //Swap pivot and left
    Integer temp = numArray.get(pivot);
//      System.out.println(numArray.size()+" "+pivot);
    int leftVal=numArray.get(left);
    numArray.set(pivot, leftVal);
    pivot=temp;
    numArray.set(left, pivot);

    Integer m=0;

    //REPEAT:
    while(true){
        int i=left+1;
        int j=right+1;

        while(numArray.get(i).intValue()<pivot){
            i++;
        }
        while(numArray.get(j).intValue()>pivot){
            j--;
        }

        if(i<j){
            //Swap i and j
            if(test) printArrayWithMessage("[SWAP] (i="+i+") and (j="+j+")", numArray);

            Integer a=numArray.get(i);
            numArray.set(i, numArray.get(j));
            numArray.set(j, a);
        }

        if(i>j){
            //Swap pivot and j
            if(test) printArrayWithMessage("[SWAP] (j="+j+") and (pivot="+left+")", numArray);

            numArray.set(left, numArray.get(j));
            numArray.set(j, pivot);
            m=j;

            break;
        }
    }

    if(test) printArrayWithMessage("Iteration Finished... Left: "+left+"  Right: "+right, numArray);


        QuickSort(numArray, left, m-1);
        QuickSort(numArray, m+1, right);

    return numArray;
}

public void benchmark(){

    for(int i=1;i<6;i++){
        //CREATE BLANK DATA STRUCTURES
        ArrayList<Integer> numList = new ArrayList<Integer>();
        Set<Integer> doublesFilter;

        if(sortMethod==RANDOM){
            doublesFilter = new HashSet<Integer>();
        }else{
            doublesFilter = new TreeSet<Integer>();
        }
        //FILL ARRAYLIST WITH UNIQUE NUMBERS
        for(int j=0;j<2500*i;j++){
            int num=r.nextInt(1000000);
            if(doublesFilter.add(num)){
                if(sortMethod==RANDOM){
                    numList.add(num);
                }
            }
        }
        if(sortMethod==SORTED){
            numList.add(0);
            numList.ensureCapacity(doublesFilter.size());
            numList.addAll(doublesFilter);
            numList.remove(0);
        }
        else if(sortMethod==REVERSE_SORTED){
            numList.add(0);
            numList.ensureCapacity(doublesFilter.size());
            numList.addAll(((TreeSet<Integer>) doublesFilter).descendingSet());
            numList.remove(0);
        }


        System.out.println("Starting sort number: ["+i+"] : "+getMode());
        numList=SlowSort(numList);
    }
}


public void printArrayWithMessage(String s, ArrayList<Integer> list){
    System.out.println(s);
    System.out.println(list);
    System.out.println();
}

public String getMode(){

    String rPivot="OFF";
    if(randomPivot) rPivot="ON";


    String sortMode="UNDEFINED";
    if(sortMethod==1)sortMode="RANDOM";
    if(sortMethod==2)sortMode="SORTED";
    if(sortMethod==3)sortMode="REVERSE SORTED";

    return "RandomPivot ["+rPivot+"] : "+"Sorting ["+sortMode+"]";
}

}

2 个答案:

答案 0 :(得分:3)

正如您所指出的,Quicksort在提供排序数组时表现最差,最终进行O(n)递归调用,因为分区在每个细分步骤中只删除一个元素。在未对数组进行排序的其他情况下,分区更有效,因此您最终会使用O(lgN)递归调用。 O(n)递归调用超过了O(lgN)调用的最大堆栈帧数。

编辑(附加说明): 在快速排序中使用随机数据透视的好处和意图之一是它确保分区/子问题的大小为O(n)而不是最坏情况下的O(1)行为后一种情况(不随机) pivot + sorted input)是您看到堆栈溢出错误的地方。

答案 1 :(得分:2)

递归实现将始终在“正确”情况下(即大型数据集)导致堆栈溢出。另外,看看类似问题的一些答案:

Got stackoverflowerror when using quickSort, can I increase the stack and the heap?

Some issues in implementing QuickSort in java