Quicksort ไม่ทำงานกับอาร์เรย์ที่เรียงลำดับ

ฉันกำลังทำโปรเจ็กต์เพื่อเปรียบเทียบอัลกอริธึม Bubblesort และ Quicksort ทุกอย่างทำงานได้ดีจนกระทั่งฉันต้องการจัดเรียงข้อมูลที่จัดเรียงด้วยวิธี Quicksort แล้ว มันขัดข้องในอาร์เรย์ขนาดใหญ่ (50k, 100k)

ในกรณีของฉัน อันดับแรก ฉันจะเรียงลำดับข้อมูลจากมากไปหาน้อย จากนั้นลองเรียงลำดับจากน้อยไปหามาก แล้วมันล้มเหลว

        read();  // just creates an array with random integers
        quickSortDSC(0, length - 1); //Here is the problem! (works if commented)
        t1 = clock();
        quickSortASC(0, length - 1);
        t2 = clock();
        cout << "Quick Sort\t: " << (t2 - t1)/CLK_TCK << " sec\n";

รหัสเต็ม:

    #include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>

using namespace std;

long length = 1000;
const long max_length = 100000;

int list[max_length];

void read()
{
    ifstream fin("random.dat", ios::binary);
    for (long i = 0; i < length; i++)
    {
        fin.read((char*)&list[i], sizeof(int));
    }
    fin.close();
}

void bubbleSort()
{
    int temp;
    for(long i = 0; i < length; i++)
    {
        for(long j = 0; j < length-i-1; j++)
        {
            if (list[j] > list[j+1])
            {
                temp        = list[j];
                list[j]     = list[j+1];
                list[j+1]   = temp;
            }
        }
    }
}


long partitionASC(long left, long right)
{
    int pivot_element = list[left];
    int lb = left, ub = right;
    int temp;

    while (left < right)
    {
        while(list[left] <= pivot_element)
            left++;
        while(list[right] > pivot_element)
            right--;
        if (left < right)
        {
            temp        = list[left];
            list[left]  = list[right];
            list[right] = temp;
        }
    }
    list[lb] = list[right];
    list[right] = pivot_element;
    return right;
}

long partitionDSC(long left, long right)
{
    int pivot_element = list[left];
    int lb = left, ub = right;
    int temp;

    while (left < right)
    {
        while(list[left] >= pivot_element)
            left++;
        while(list[right] < pivot_element)
            right--;
        if (left < right)
        {
            temp        = list[left];
            list[left]  = list[right];
            list[right] = temp;
        }
    }
    list[lb] = list[right];
    list[right] = pivot_element;
    return right;
}
//ascending order
void quickSortASC(long left, long right)
{
    long pivot;
    if (left < right)
    {
        pivot = partitionASC(left, right);
        quickSortASC(left, pivot-1);
        quickSortASC(pivot+1, right);
    }
}

//descending order
void quickSortDSC(long left, long right)
{
    long pivot;
    if (left < right)
    {
        pivot = partitionDSC(left, right);
        quickSortDSC(left, pivot-1);
        quickSortDSC(pivot+1, right);
    }
}

int main()
{
    double t1, t2;

    for (length = 1000; length <= max_length; )
    {
        cout << "\nLength\t: " << length << '\n';

        read();
        t1 = clock();
        bubbleSort();
        t2 = clock();
        cout << "Bubble Sort\t: " << (t2 - t1)/CLK_TCK << " sec\n";


        read();
        quickSortDSC(0, length - 1); //Here is the problem!
        t1 = clock();
        quickSortASC(0, length - 1);
        t2 = clock();
        cout << "Quick Sort\t: " << (t2 - t1)/CLK_TCK << " sec\n";

        if(length == max_length)
            break;

        switch (length)
        {
        case 1000 :
            length = 10000;
            break;
        case 10000 :
            length = 50000;
            break;
        case 50000 :
            length = 100000;
            break;
        }
    }

    return 0;
}

person Adomas    schedule 29.04.2018    source แหล่งที่มา
comment
คุณอาจประสบปัญหาสแต็กโอเวอร์โฟลว์ ใช้ดีบักเกอร์และดูว่าคุณมีสแต็กเฟรมจำนวนเท่าใดเมื่อเกิดข้อขัดข้อง   -  person Stephen Newell    schedule 29.04.2018
comment
หากคุณใช้องค์ประกอบซ้ายสุดเป็นจุดหมุน และนั่นคือค่าที่น้อยที่สุดเสมอ คุณจะมีกรณีที่แย่ที่สุดสำหรับการเรียงลำดับอย่างรวดเร็ว จะไม่แปลกใจเลยหากผลลัพธ์นั้นทำให้เกิดการเรียกซ้ำถึง 100,000 ระดับ   -  person Bo Persson    schedule 29.04.2018
comment
Stephen ฉันค่อนข้างใหม่กับ codeblocks ที่ยังไม่แน่ใจว่าจะใช้ดีบักเกอร์อย่างไร โบ คุณคิดว่าฉันควรเปลี่ยนอะไรเพื่อหลีกเลี่ยงสิ่งนั้น   -  person Adomas    schedule 29.04.2018
comment
มีวิธีอื่นๆ ในการเลือกจุดเปลี่ยนสำคัญ เช่น ค่ามัธยฐานของ 3 และในแอปพลิเคชันจริง คุณจะไม่ใช้การเรียงลำดับอย่างรวดเร็วหากข้อมูลมีแนวโน้มที่จะเรียงลำดับแล้ว ฉันมีความสนใจในโปรแกรมหมากรุกที่การวิเคราะห์พฤติกรรมการสร้างการเคลื่อนไหวสร้างรายการการเคลื่อนไหวที่ค่อนข้างดี เหมือนกับที่ PxQ มาก่อนเสมอ ถ้าอย่างนั้น ฟองสบู่ที่น่าสะพรึงกลัวจะสมบูรณ์แบบหากมีสถานที่เพียงหนึ่งหรือสองแห่งที่ต้องสัมผัส   -  person Bo Persson    schedule 29.04.2018


คำตอบ (2)


เมื่อเลือกองค์ประกอบแรกของอาร์เรย์เป็นเดือย คุณจะเข้าสู่ลักษณะการทำงานของกรณีที่แย่ที่สุดในการเรียงลำดับอย่างรวดเร็วเมื่ออาร์เรย์ถูกจัดเรียงแล้ว ดังนั้นคุณจะได้รับพฤติกรรม O(n2) (และแย่กว่านั้นคือพื้นที่สแต็ก O(n) ซึ่งอาจทำให้คุณมีสแต็กล้น)

เพื่อหลีกเลี่ยงปัญหาดังกล่าว ให้เลือกจุดหมุนอื่น โดยปกติแล้วเราจะเลือกองค์ประกอบตรงกลางเป็นจุดหมุน:

int pivot_element = list[(left+right)/2];

หรือองค์ประกอบสุ่ม:

int pivot_element = list[left + random()%(right+1-left)];
person Chris Dodd    schedule 29.04.2018
comment
นั่นไม่ได้แก้ไขปัญหาได้อย่างสมบูรณ์ คุณยังคงสามารถเผชิญกับการกระจายองค์ประกอบดังกล่าวซึ่งจะส่งผลให้เกิดการโอเวอร์โฟลว์ของสแต็ก - person Zereges; 29.04.2018
comment
@Zereges: ใช่นั่นคือธรรมชาติของ Quicksort แต่สำหรับกรณี OPs มันก็จะไม่เป็นไร - person Chris Dodd; 29.04.2018
comment
ขอบคุณคริส! แก้ไขปัญหาแล้ว - person Adomas; 30.04.2018

ตัวอย่างเช่น.. สมมติว่า {3, 6, 4, 7} เป็นกรณีนี้ ไม่ว่าคุณจะเลือก Single pivot หรือ Double pivot QuickSort หากคุณสร้างองค์ประกอบแรกเป็น pivot เสมอ ดังนั้นสำหรับอาร์เรย์ที่เรียงลำดับ ก็มีโอกาสพอสมควรที่คุณอาจเผชิญกับ stack-overflow หรือ O(n2)

ดังนั้น การสุ่มจุดหมุน/วินาทีในแต่ละรอบการเรียกซ้ำเป็นการป้องกันการเข้าสู่ O(n2) หรือการวนซ้ำไม่รู้จบในกรณีที่เลวร้ายที่สุด

ตัวอย่างด้านล่างนี้จะอธิบายกรณีที่แย่ที่สุดได้ดี..

แพ็คเกจ algo.sorting.rev;

นำเข้า java.util.Arrays;

DualPivotQuickSort คลาสสาธารณะ {

public static void main(String[] args) {
    int[] input = new int[] { 8, 6, 2, 4, 17,    3, 10, 19, 21, 13,   9, 19, 14, 13, 7,   17 };
    quicksort(input, 0, input.length - 1);
    System.out.println(Arrays.toString(input));
}

/**
 * 3 segments are as below.
 * 
 * | item < pivotL | pivotL <= item <= pivotR | item > pivotR |
 * 
 * |pivotLPos . . . j-1|j . . . k|k+1 . . . pivotRPos|
 * 
 * 
 * @param a
 * @param pivotLPos
 * @param pivotRPos
 */
private static void quicksort(int [] a, int pivotLPos, int pivotRPos){
    int size  = pivotRPos - pivotLPos + 1;

    if(size < 3)
        return;

    int pivotL = a[pivotLPos];
    int pivotR = a[pivotRPos];

    if(pivotL > pivotR)
        swapContent(a, pivotLPos, pivotRPos);

    int i = pivotLPos + 1;
    int j = pivotRPos - 1;
    int k = pivotRPos;

    while(i <= j){

        while(i < pivotRPos && a[i] < pivotL)
            i++;

        while(j >= pivotLPos && a[j] >= pivotL){
            if(a[j] <= pivotR)
                j--;
            else{  
                // adding to segment3
                swapContent(a, j, k);
                j--;
                k--;
            }
        }

        if(i < j){
            swapContent(a, i, j);
            i++;

            if(a[j] > pivotR){
                // adding to segment3
                swapContent(a, j, k);
                k--;
            }
            j--;
        }
    }

    swapContent(a, j, pivotLPos);

    if(size > 3){
        if(j - pivotLPos >= 3)
            quicksort(a, pivotLPos, j-1);  // recursion on seg1
        if(k - j >= 3)
            quicksort(a, j, k);  // recursion on seg2
        if(j - pivotRPos >= 3)
            quicksort(a, k+1, pivotRPos);  // recursion on seg3
    }
}

private static void swapContent(int [] a, int pos1, int pos2){
    int b = a[pos1];
    int c = a[pos2];

    b ^= c;
    c ^= b;
    b ^= c;

    a[pos1] = b;
    a[pos2] = c;
}

}

person Ashvini    schedule 30.05.2020