快速排序的一点思考

Hello, 快速排序

快速排序(quick sort)的最好、平均时间复杂度为,最坏为,并且在平均情况下,复杂度系数相比堆排序更小,因而速度更快。
快速排序的核心在于分区函数partition设计。关于实现方式,通常有双边搜索单边搜索。在单边搜索中,通常选择pivot值(分位点、枢纽)为数组的第一个值/最后一个值,也可以选择随机值。而产生随机值通常是耗时操作。

优化一下?

选择了不好的pivot会导致最坏的时间复杂度。不好的pivot通常是数组的最大/最小元素,以此分区会使两边元素数量相差过大。在一个接近完全排序的数组中,简单选择首尾元素容易导致最坏情况发生。而随机化如前所述并不是一个好办法,并且也存在取到最值的情况。
可以认为一个规模较大的数组,较小概率是一个接近完全排序的数组,因此使用快速排序是合理的。而在小规模数组中,更容易出现接近完全排序的情况,这是快速排序力所不及之处,但却是插入排序的用武之地。

插入排序:考虑一下人家嘛~

插入排序的平均时间复杂度为,但最好时间复杂度仅为,非常适合小规模接近完全排序的数组排序。快速排序的劣势正是插入排序所长。

标准库排序函数的极简实现

C++std::sort函数中,采用了多种排序算法组合的方式:

  • 当数组长度size < 32时,选择插入排序
  • size >= 32
    • 初始选择快速排序
      • 选择pivot应用了median of three方法,即选取首、中、尾3个元素,选择它们的中间值
      • 而当size较大的时候,在数组中均匀选择9个元素,选择其中间值作为pivot
    • 当递归深入过深,超过设定值时,选择堆排序

一点讨论

Q: 堆排序在任何情况下的时间复杂度都是,不会出现快速排序恶化的情况,为什么不直接选择堆排序作为主要实现方式呢?

A: 那当然是快速排序更(废话,不然为什么叫快速排序)。并且最坏情况由于引入了med3而几乎不会出现(我猜的...),性能得到保证。不使用随机pivot也是避免额外开销。

附上代码 in c++

/* 这个实现最好看,不接受反驳!!! */
int partition(std::vector& data, int start, int end)
{
    int pivot = data[end];
    int mark = start;
    for (int i = start; i < end; ++i)
        if (data[i] < pivot)
            std::swap(data[i], data[mark++]);
    
    std::swap(data[end], data[mark]);
    return mark;
}

void quickSort(std::vector& data, int start, int end)
{
    if (start >= end) return;
    
    int pivot = partition(data, start, end);
    quickSort(data, start, pivot - 1);
    quickSort(data, pivot + 1, end);
}

void quickSort(std::vector& data)
{
    quickSort(data, 0, data.size() - 1);
}

参考了别人的

(1)STL sort 函数实现详解

(2)这或许是东半球分析十大排序算法最好的一篇文章

(3)十大经典排序算法

最后

如果有错,评论区相见

你可能感兴趣的:(快速排序的一点思考)