快速排序

特点

对于有n个数的数组来说,快速排序最坏情况下的时间复杂度是O(n^2)。但是,快速排序通常是在实际应用中最好的选择,因为它的平均性能非常好: 它的期望时间复杂度是 O(n lgn),而且,O(n lgn)中隐含的常数因子很小。它还是可以进行原址排序。STL algorithm中的sort函数就是使用快速排序。

过程

快速排序也是使用分治思想。其排序步骤为:

分解:数组 A[p...r] 被划分为两个子数组 A[p...q-1] 和 A[q+1...r],使得 A[p...q-1]中的每个元素都小于等于A[q],A[q+1...r] 中的元素都大于 A[q]。

解决:通过快速排序,对两个子数组也排序

合并:因为是子数组是原址排序,所以不需要合并

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int partition(int* arr, int p, int r)
{
    int key = arr[r];
    int i = p - 1;

    for (int j = p; j < r; j++)
    {
        if (arr[j] < key)
        {
            i++;
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    }
    int tmp = arr[i+1];
    arr[i+1] = arr[r];
    arr[r] = tmp;

    return i+1;
}

void quick_sort(int* arr, int p, int r)
{
    if (p < r)
    {
        int q = partition(arr, p, r);
        quick_sort(arr, p, q-1);
        quick_sort(arr, q+1, r);
    }
}

int main()
{
    int arr[] = {2,4,62,3,4,3,1,7,8,9,5};
    int len = sizeof(arr) / sizeof(int);

    quick_sort(arr, 0, len-1);

    for (int i = 0; i < len; ++i)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

 

quick_sort函数入参表示:数组、开始下标、结束下标。下标都是闭区间。

关键部分是partition函数,《算法导论》中的图:

性能

快速排序的运行时间依赖于划分是否平衡。如果划分平衡,时间复杂度O(n lgn),如果不平衡,时间复杂度 O(n^2)

在最坏情况,就是划分产生的两个子问题分别包含 n-1 个元素和 0 个元素。如果每次递归都出现了这种不平衡,划分操作时间复杂度 O(n).

T(n) = T(n-1) + O(n)

所以时间复杂度为 1+2+3+...+n = O(n^2)

最好情况下,partition得到的两个子问题的规模都不大于n/2.这时,算法运行时间关系是:

T(n) = 2T(n-1) + O(n)

时间复杂度为 O(n lgn),不过,复杂度的常数因子比堆排序、归并排序都要小

稳定性

快速排序是不稳定的。它可以保证 A[p...q] 的稳定性,但不能保证 A[q...r]的稳定性。

比如数组:[2,7,7,1,1,5,6,4],快排之后,[2,1,1,4]保持顺序稳定,但是第一个7到了最后一位。

posted @ 2018-09-18 22:55  二狗啸地  阅读(189)  评论(0编辑  收藏  举报