簡體   English   中英

排序數組的QuickSort堆棧溢出(適用於其他數據集)

[英]QuickSort stack overflow for sorted arrays (works for other data sets)

因此,我盡力優化我的Quicksort算法以盡可能高效地運行,即使對於已排序或接近排序的數組,使用三個值的中間值的樞軸,以及對小分區大小使用插入排序。 我已經測試了我的代碼用於大型隨機值數組並且它可以工作,但是當我傳遞已經排序的數組時,我得到了一個堆棧溢出錯誤(具有諷刺意味的是它導致我找到了這個網站)。 我認為這是我的遞歸調用的一個問題(我知道分區至少適用於其他數據集),但我不知道要改變什么。

這是我第一學期數據結構課程的一部分,因此任何代碼審查也會有所幫助。 謝謝。

public void quickSort(ArrayList<String> data, int firstIndex, int numberToSort) {
    if (firstIndex < (firstIndex + numberToSort - 1))
        if (numberToSort < 16) {
            insertionSort(data, firstIndex, numberToSort);
        } else {
            int pivot = partition(data, firstIndex, numberToSort);
            int leftSegmentSize = pivot - firstIndex;
            int rightSegmentSize = numberToSort - leftSegmentSize - 1;
            quickSort(data, firstIndex, leftSegmentSize);
            quickSort(data, pivot + 1, rightSegmentSize);
        }
}



public int partition(ArrayList<String> data, int firstIndex, int numberToPartition) {
    int tooBigNdx = firstIndex + 1;
    int tooSmallNdx = firstIndex + numberToPartition - 1;

    String string1 = data.get(firstIndex);
    String string2 = data.get((firstIndex + (numberToPartition - 1)) / 2);
    String string3 = data.get(firstIndex + numberToPartition - 1);
    ArrayList<String> randomStrings = new ArrayList<String>();
    randomStrings.add(string1);
    randomStrings.add(string2);
    randomStrings.add(string3);
    Collections.sort(randomStrings);
    String pivot = randomStrings.get(1);
    if (pivot == string2) {
        Collections.swap(data, firstIndex, (firstIndex + (numberToPartition - 1)) / 2);
    }
    if (pivot == string3) {
        Collections.swap(data, firstIndex, firstIndex + numberToPartition - 1);
    }
    while (tooBigNdx < tooSmallNdx) {
        while ((tooBigNdx < tooSmallNdx) && (data.get(tooBigNdx).compareTo(pivot) <= 0)) {
            tooBigNdx++;
        }
        while ((tooSmallNdx > firstIndex) && (data.get(tooSmallNdx).compareTo(pivot) > 0)) {
            tooSmallNdx--;
        }
        if (tooBigNdx < tooSmallNdx) {// swap
            Collections.swap(data, tooSmallNdx, tooBigNdx);
        }
    }
    if (pivot.compareTo(data.get(tooSmallNdx)) >= 0) {
        Collections.swap(data, firstIndex, tooSmallNdx);
        return tooSmallNdx;
    } else {
        return firstIndex;
    }
}

您可以避免堆棧溢出而不會過多地更改算法。 訣竅是在最大的分區上進行尾部調用優化,並且只在最小的分區上使用遞歸。 這通常意味着你必須對你的變化ifwhile 我現在無法真正測試java代碼,但它應該類似於:

public void quickSort(ArrayList<String> data, int firstIndex, int numberToSort) {
    while (firstIndex < (firstIndex + numberToSort - 1))
        if (numberToSort < 16) {
            insertionSort(data, firstIndex, numberToSort);
        } else {
            int pivot = partition(data, firstIndex, numberToSort);
            int leftSegmentSize = pivot - firstIndex;
            int rightSegmentSize = numberToSort - leftSegmentSize - 1;

            //only use recursion for the smallest partition
            if (leftSegmentSize < rightSegmentSize) {
                quickSort(data, firstIndex, leftSegmentSize);
                firstIndex = pivot + 1;
                numberToSort = rightSegmentSize;
            } else {
                quickSort(data, pivot + 1, rightSegmentSize);
                numberToSort = leftSegmentSize;
            }
        }
}

這可以確保調用堆棧大小最多為O(log n) ,因為在每次調用時,您只對最多n/2大小的數組使用遞歸。

partition方法中,有時使用范圍之外的元素:

String string1 = data.get(firstIndex);
String string2 = data.get((firstIndex + (numberToPartition - 1)) / 2);
String string3 = data.get(firstIndex + numberToPartition - 1);

(firstIndex + (numberToPartition - 1)) / 2不是中間元素的索引。 那將是(firstIndex + (firstIndex + (numberToPartition - 1))) / 2

= firstIndex + ((numberToPartition - 1) / 2)

實際上,如果firstIndex > n/2 (其中n是輸入中元素的數量),則使用索引小於firstIndex的元素。 對於排序數組,這意味着您在firstIndex選擇元素作為pivot元素。 因此,您將獲得遞歸深度

<代碼>歐米茄(n)的</代碼>

這導致堆棧溢出足夠大的輸入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM