Quicksort tidak berfungsi dengan array yang diurutkan

Saya sedang melakukan proyek untuk membandingkan algoritma Bubblesort dan Quicksort. Semuanya berfungsi dengan baik sampai saya ingin mengurutkan data yang sudah diurutkan dengan metode Quicksort. Itu mogok pada array besar (50k, 100k).

Dalam kasus saya, pertama-tama saya mengurutkan data dalam urutan menurun dan kemudian saya mencoba mengurutkan dalam urutan menaik dan kemudian macet.

        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";

Kode lengkap:

    #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 sumber
comment
Anda mungkin mengalami stack overflow. Gunakan debugger dan lihat berapa banyak bingkai tumpukan yang Anda miliki saat kerusakan terjadi.   -  person Stephen Newell    schedule 29.04.2018
comment
Jika Anda menggunakan elemen paling kiri sebagai pivot, dan elemen tersebut selalu merupakan nilai terkecil, Anda mempunyai kasus terburuk untuk quicksort. Tidak mengherankan jika hal itu menghasilkan 100.000 tingkat rekursi.   -  person Bo Persson    schedule 29.04.2018
comment
Stephen, saya cukup baru dengan blok kode, belum yakin bagaimana cara menggunakan debugger. Bo, menurutmu apa yang harus aku ubah untuk menghindari hal itu?   -  person Adomas    schedule 29.04.2018
comment
Ada cara lain untuk memilih pivot, seperti median tiga. Dan dalam aplikasi nyata Anda tidak akan menggunakan quicksort jika datanya cenderung sudah diurutkan. Saya tertarik dengan program catur di mana heuristik pembangkitan gerakan menghasilkan daftar langkah yang cukup bagus, seperti PxQ yang selalu menjadi yang pertama. Maka bubblesort yang ditakuti sangat cocok jika hanya ada satu atau dua tempat untuk diperbaiki.   -  person Bo Persson    schedule 29.04.2018


Jawaban (2)


Dengan memilih elemen pertama array sebagai pivot, Anda mengalami perilaku terburuk quicksort ketika array sudah diurutkan. Jadi Anda mendapatkan perilaku O(n2) (dan lebih buruk lagi, ruang tumpukan O(n), yang mungkin memberi Anda tumpukan meluap).

Untuk menghindarinya, pilih pivot yang berbeda. Biasanya seseorang akan memilih elemen tengah sebagai poros:

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

atau elemen acak:

int pivot_element = list[left + random()%(right+1-left)];
person Chris Dodd    schedule 29.04.2018
comment
Itu tidak menyelesaikan masalah sepenuhnya. Anda masih dapat menemukan distribusi elemen sedemikian rupa sehingga hal ini akan mengakibatkan stack overflow. - person Zereges; 29.04.2018
comment
@Zereges: ya, itulah sifat quicksort. Tapi untuk kasus OP, akan baik-baik saja. - person Chris Dodd; 29.04.2018
comment
Terima kasih, Kris! Memecahkan masalah - person Adomas; 30.04.2018

Misalnya.. Misalkan {3, 6, 4, 7} adalah kasusnya. Baik Anda memilih Pivot tunggal atau Pivot ganda QuickSort, jika Anda selalu menjadikan elemen pertama sebagai pivot maka untuk array yang diurutkan, ada kemungkinan besar Anda akan menghadapi stack-overflow atau O(n2).

Jadi, mengacak pivot/s di setiap siklus rekursi adalah pencegahan memasuki O(n2) atau loop tak berujung dalam kasus terburuk.

Contoh di bawah ini akan menjelaskan dengan baik kasus terburuk..

paket algo.sorting.rev;

import java.util.Array;

kelas publik 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