快速排序不适用于排序数组

Quicksort doesn't work with sorted array

本文关键字:数组 排序 适用于 不适用 快速排序      更新时间:2023-10-16

我正在做一个比较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 Sortt: " << (t2 - t1)/CLK_TCK << " secn";

完整代码:

    #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 << "nLengtht: " << length << 'n';
        read();
        t1 = clock();
        bubbleSort();
        t2 = clock();
        cout << "Bubble Sortt: " << (t2 - t1)/CLK_TCK << " secn";

        read();
        quickSortDSC(0, length - 1); //Here is the problem!
        t1 = clock();
        quickSortASC(0, length - 1);
        t2 = clock();
        cout << "Quick Sortt: " << (t2 - t1)/CLK_TCK << " secn";
        if(length == max_length)
            break;
        switch (length)
        {
        case 1000 :
            length = 10000;
            break;
        case 10000 :
            length = 50000;
            break;
        case 50000 :
            length = 100000;
            break;
        }
    }
    return 0;
}

通过选择数组的第一个元素作为透视,当数组已经排序时,您可以达到快速排序的最坏情况行为。 所以你会得到 O(n2( 行为(更糟糕的是,O(n( 堆栈空间,这可能会给你一个堆栈溢出(。

为避免这种情况,请选择其他枢轴。 通常人们会选择中间元素作为枢轴:

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

或随机元素:

int pivot_element = list[left + random()%(right+1-left)];

例如..假设 {3, 6, 4, 7} 是这种情况。 无论您选择单透视还是双透视快速排序,如果您总是将第一个元素作为透视,那么对于排序数组,您很有可能遇到堆栈溢出或 O(n2(。

因此,在每个递归循环中随机化枢轴是防止在最坏的情况下进入 O(n2( 或无限循环。

下面的例子将很好地解释最坏的情况。

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

}