Quicksort не работает с отсортированным массивом

Я делаю проект по сравнению алгоритмов Bubblesort и 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
Стивен, я новичок в кодовых блоках, еще не знаю, как использовать отладчик. Бо, как ты думаешь, что мне следует изменить, чтобы этого избежать?   -  person Adomas    schedule 29.04.2018
comment
Есть и другие способы выбора точки поворота, например медиана из трех. А в реальном приложении вы просто не стали бы использовать быструю сортировку, если данные, как правило, уже отсортированы. Меня интересуют шахматные программы, в которых эвристика генерации ходов дает довольно хороший список ходов, например, PxQ всегда идет первым. Тогда ужасная пузырьковая сортировка идеальна, если нужно подправить всего одно или два места.   -  person Bo Persson    schedule 29.04.2018


Ответы (2)


Выбирая первый элемент массива в качестве опорного, вы попадаете в наихудшее поведение быстрой сортировки, когда массив уже отсортирован. Таким образом, вы получаете поведение O (n 2) (и, что еще хуже, пространство стека 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: да, такова природа быстрой сортировки. Но для случая OP все будет нормально. - person Chris Dodd; 29.04.2018
comment
Спасибо, Крис! Решил проблему - person Adomas; 30.04.2018

Например .. Предположим, что {3, 6, 4, 7} имеет место. Независимо от того, выбираете ли вы Single pivot или Double pivot QuickSort, если вы всегда делаете первый элемент как сводный, тогда для отсортированного массива есть хорошие шансы, что вы можете столкнуться с переполнением стека или O (n2).

Таким образом, рандомизация точки поворота в каждом цикле рекурсии предотвращает попадание в O (n2) или бесконечный цикл в худшем случае.

Пример ниже хорошо объяснит худший случай.

пакет algo.sorting.rev;

import java.util.Arrays;

public class 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