0

I am doing a project to compare Bubblesort and Quicksort algorithms. Everything works fine until I want to sort data which has been already sorted with Quicksort method. It crashes on large arrays (50k, 100k).

In my case firstly I sort data in descending order and then I try sorting in ascending order and then it crashes.

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

Full code:

    #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;
}
Adomas
  • 21
  • 1
  • 8
  • 1
    You might be running into a stack overflow. Use a debugger and see how many stack frames you have when the crash occurs. – Stephen Newell Apr 29 '18 at 19:33
  • 2
    If you use the leftmost element as the pivot, and that is always the smallest value, you have the absolute worst case for quicksort. Wouldn't be surprised if that results in 100.000 levels of recursion. – Bo Persson Apr 29 '18 at 19:44
  • Stephen, I am quite new with codeblocks not sure how to use debugger yet. Bo, what do you think I should change to avoid that? – Adomas Apr 29 '18 at 19:50
  • There are other ways to select the pivot, like [median of three](https://stackoverflow.com/questions/7559608/median-of-three-values-strategy). And in a real application you just wouldn't use quicksort if the data tends to be already sorted. I have an interest in chess programs where move generation heuristics produce a pretty good move list, like PxQ always goes first. Then the dreaded bubblesort is perfect if there is just one or two places to touch up. – Bo Persson Apr 29 '18 at 20:13

2 Answers2

3

By choosing the first element of the array as the pivot, you hit the quicksort worst-case behavior when the array is already sorted. So you get O(n2) behavior (and worse, O(n) stack space, which probably gives you a stack overflow).

To avoid that, choose a different pivot. Normally one would choose the middle element as the pivot:

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

or a random element:

int pivot_element = list[left + random()%(right+1-left)];
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
0

For example.. Suppose {3, 6, 4, 7} is the case. Whether you choose Single pivot or Double pivot QuickSort, if you always make first element as pivot then for a sorted array there are fair chances that you may face stack-overflow or O(n2).

So, randomising the pivot/s in each recursion cycle is the prevention from entering into O(n2) or endless loop in worst case.

Below example will explain well the worst case..

package 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;
}

}

Ashvini
  • 1
  • 2