这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
上篇文章介绍了一些基本的排序算法和它们在不同情况下的表现,我们需要根据不同情况选择不同算法才能得到较好的性能,能否设计一种算法让其在各种情况下都表现较优呢?
一、初步思路
1.对于短序列(小于一定长度)我们可以使用插入排序。
不同场景对短序列的定义可能不同,这里我们选择24。
2.对于大部分其他情况,可以使用快速排序保证整体的综合性能。
3.当快排表现不佳的时候,可以使用堆排序保证最坏情况下时间复杂度仍为O(n*logn)。
当最终哨兵pivot的位置离序列两端很近(距离小于length/8)时,判断其表现不佳,当这种情况的次数累计达到limit时,切换到堆排序。
二、进一步改进
1.pivot的选择
pivot的理想选择是选择数组的中位数,这样每次都正好将数组分成长度相同的两部分,此时快速排序的性能表现最好。因此,我们需要近似地找到数组的中位数作为pivot。
当序列长度较短(<=8)时,可以简单地选择固定元素;
当序列长度中等(<=50)时,可以采样三个元素,选择其中位数;
当序列长度较长(>50)时,可以采样九个元素,选择其中位数。
此外,当我们发现采样的元素完全逆序的时候,此时序列也可能完全逆序,可以翻转数组;当采样的元素的元素完全正序的时候,此时序列可能已经有序或接近有序,可以采用(特殊的,有限制次数的)插入排序。
2.处理重复元素
重复元素较多也会影响排序的效率(重复的元素在快排中全分在一边,即使它是中位数也会导致两边不平衡),因此需要处理。
当两次分割选择的pivot的值相同时,可以认为此时pivot为重复元素。我们需要将重复元素放在一起,避免对后续排序的干扰。