performance - 快速排序与。归并排序性能分析

标签 performance sorting quicksort mergesort

归并排序的最坏情况复杂度为 O(logN),而快速排序的最坏情况复杂度为 O(N^2),因此理论上归并排序的性能应该优于快速排序。但我听说由于一些复制开销,大多数情况下快速排序优于合并排序。 See the reference .

然后我决定实现和测试,下面是我的完整 C 源代码,

来源

#include <stdio.h>
#include <time.h>

#define SZ 10000000
#define MOD 10000007
#define i64 long long int

i64 nums[SZ];

i64 L[SZ], R[SZ];

i64 seed = 0xff;
i64 srand(){
    seed = (seed + 17 * seed) % MOD;
    return seed;
}

void make(){
    for (register int i = 0; i < SZ; i++)
        nums[i] = srand() % MOD;
}

void swap(i64 *a, i64 *b){
    i64 t = *a;
    *a = *b;
    *b = t;
}

int pivote(int s, int e){

    //int p = s + srand() % (e - s + 1);
    int p = s + (e - s) / 2;
    //int p = s;
    //int p = e;

    i64 v = nums[p];
    int c = s;
    swap(nums + p, nums + e);
    for (register int i = s; i < e; i++){
        if (nums[i] < v){
            swap(nums + i, nums + c);
            c++;
        }
    }
    swap(nums + c, nums + e);
    return c;
}

void qsort(int s, int e){

    if (s < e){
        int p = pivote(s, e);
        qsort(s, p - 1);
        qsort(p + 1, e);
    }
}

void merge(i64 arr[], int l, int m, int r){
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;

    for (i = 0; i < n1; i++)
        L[i] = arr[l + i];
    for (j = 0; j < n2; j++)
        R[j] = arr[m + 1 + j];

    i = 0;
    j = 0;
    k = l;
    while (i < n1 && j < n2)
    {
        if (L[i] <= R[j])
        {
            arr[k] = L[i];
            i++;
        }
        else
        {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    while (i < n1)
    {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2)
    {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(i64 arr[], int l, int r){
    if (l < r){
        int m = l + (r - l) / 2;

        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}


void testQsort(){
    double s, e;

    make();

    s = clock();
    qsort(0, SZ - 1);
    e = clock();
    printf("qsort random: %Lf ms\n", (e - s) / 1);

    s = clock();
    qsort(0, SZ - 1);
    e = clock();
    printf("qsort sorted: %Lf ms\n", (e - s) / 1);

}

void testMsort(){
    double s, e;

    make();

    s = clock();
    mergeSort(nums, 0, SZ - 1);
    e = clock();
    printf("msort random: %Lf ms\n", (e - s) / 1);

    s = clock();
    mergeSort(nums, 0, SZ - 1);
    e = clock();
    printf("msort sorted: %Lf ms\n", (e - s) / 1);
}

int main(){

    testMsort();
    testQsort();

    return 0;
}

1000 万个元素的结果:

msort random: 4596.000000 ms
msort sorted: 3354.000000 ms
qsort random: 7637.000000 ms
qsort sorted: 5074.000000 ms

我用过四个版本的快速排序,

  • 摆在第一位
  • 在最后一个位置旋转
  • 以中间位置为中心
  • 在随机位置旋转

似乎没有一个版本的快速排序优于归并排序。 谁能说出为什么提到快速排序优于归并排序?

我的快速排序实现有什么问题吗?

更新1

根据下面提到的@rcgldr 的回答,我测试了以下版本的快速排序,它最终优于任何版本的合并排序。

void qsort3(int s, int e){
    if (s < e){
        i64 p = nums[(s + e) / 2];
        int i = s - 1;
        int j = e + 1;
        while (true){
            while (nums[++i] < p);
            while (nums[--j] > p);
            if (i >= j) break;
            swap(nums + i, nums + j);
        }
        qsort3(s, j);
        qsort3(j + 1, e);
    }
}

最佳答案

问题的快速排序示例基于 Lomuto 分区方案,它比 Hoare 分区方案慢。链接到 Hoare 分区方案的示例:

QuickSort with middle elemenet as pivot

合并排序的例子是不断地创建子数组和复制数据。一种更有效的方法是对数组进行一次分配,然后根据自上而下归并排序的递归级别或自下而上归并排序的遍数更改归并方向。链接到显示自下而上和自上而下合并排序的 Java 源代码。这可以很容易地转换为 c:

'MergeSort Algorithm' - What's the better implementation in JAVA?

至于相对性能,一个简单的快速排序(例如此答案中链接的排序)比用于对整数或 float 等简单元素数组进行排序的基本合并排序大约 15%。但是,如果增强快速排序以避免 O(n^2) 的最坏情况时间复杂度,则优势会降低,主要优势是它不需要合并排序所需的 O(n) 空间来进行合并操作。一般来说,归并排序比快速排序做更多的移动但更少的比较。如果对指向对象的指针数组进行排序,比较开销会大于移动指针所需的时间,并且归并排序最终会更快。另一方面,对指向对象的指针数组进行排序涉及随机访问这些对象,这对缓存不友好,并且排序对象而不是指针更快,除非对象相当大(权衡通常是128 到 256 字节,具体取决于系统)。

关于performance - 快速排序与。归并排序性能分析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52601913/

相关文章:

MySQL:使用 LONGTEXT 而不是 MEDIUMTEXT 是否缺乏性能?

performance - Kotlin:coroutineScope 比 GlobalScope 慢

c# - 具有自定义数据类型的 DataTable 列

java - 多线程快速排序没有给出预期的答案

c - 如何在C中对结构体指针数组进行快速排序?

java - Android RecyclerView.Adapter更改图标onClick

android - 多重通知--android

用于存储排序的唯一值的 C++ 容器,具有不同的谓词以实现排序和唯一性

java - main 中的 Arrays.sort() 错误

c - 为什么这个快速排序实现给出正确的输出而不是垃圾值?