这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记。
今天趁着课程回放权限开放温习了数据结构与算法这节视频。数据结构与算法的学习是每一个程序员必不可少的一个环节。本次笔记来源于字节青训营数据结构与算法这堂课,主要围绕了相关排序算法进行了讲解。
1.经典排序算法
1.1 插入排序
插入排序可以看成一个洗牌的过程,假设数组当前有序边界为index(index左侧的元素是有序的,index右侧的元素是无序的)。插入排序算法就是把边界index右侧第一个元素逐个与左侧的进行比较交换,再将index往右移动。
插入排序的最好时间复杂度为O(N),发生在数组有序的情况。
平均时间复杂度为O(N^2)。
最坏情况为O(N^2),发生在逆序的情况。
1.2 快速排序
快速排序的思想属于分治思想,不断分割序列直到序列整体有序。
最好情况下的时间复杂度:O(N*logN)
平均时间复杂:O(N*logN)
最坏时间复杂度:O(N^2)
1.3 堆排序
堆排序借助于大顶堆(或者小顶堆),每一次都把对顶的数据取出,并将最后一个元素放到对顶(放到对顶和还要进行一次“堆化”)。
堆排序最好情况,平均情况和最坏情况的时间复杂度都为O(N*logN)。
经过实验对比,在短序列的情况下,插入排序性能更优,而在较长序列情况下,快排和堆排性能接近并且都比插入排序好。
2. pdqsort
pdqsort是一种不稳定的混合排序算法,对常见的序列类型做了特殊的优化,使得在不同条件下都拥有不错的性能。
pdqsort结合了插入排序,快速排和堆排序三者的优点。 在短序列情况下,使用插入排序;其他情况使用快速排序保证整体的性能, 当快速排序表现性能不佳时,使用堆排序保证最坏情况下的时间复杂度。
如何得知快排的表现不佳呢? 当最终pivot的位置距离序列两端很近的时候(小于length / 8)判定为不佳,当性能不佳的次数达到一定limit后,转为堆排序。
在快排中,pivot的选择也会影响程序性能,一般有以下版本:
- 使用首个pivot
- 遍历数组,寻找中位数
对于pivot的选择,通常需要平衡寻找pivot所需要的开销和pivot带来的性能优化,所以通常选择 近似中位数。
- 对于中序列,采样三个元素
- 对于长序列,采样九个元素
3. 思考与总结
pdqsort的本质其实就是吸收不同算法在不同情况下的最优性能的一种混合算法。通过观看源码,发现很多语言自带的排序算法都是一种混合排序(比如c++,Java, Go等)。而pdqsort的难点(也是关键点)是如何确定当前待排序序列的状态。