这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记
为什么要学习数据结构和算法
真实案例:实现抖音直播间排行榜
规则:在某个时间段内,直播间礼物数TOP10房间获得奖励,需要在每个直播间展示排行榜
解决方案
- 礼物数量存储在 Redis-zset 中,使用 skiplist 使元素整体有序
- 使用 Redis 集群,避免单机压力过大,使用 主从算法,分片算法
- 保证集群原信息稳定,使用 一致性算法
- 后端使用 缓存算法(LRU) 降低 Redis 压力,展示房间排行榜
什么是最快的排序算法
单线程下,编程语言自带的排序算法
python 的 timsort
C++ introsort
Rust 的 pdqsort
Go的排序算法有没有提升的空间?
经典排序算法
插入排序
- 最优时间复杂程 O(n),
- 平均时间复杂度 O(n^2)
- 最坏时间复杂度 O(n^2)
- 但是已经是比较排序中最优的算法了
快速排序
采用分治思想
- 最优时间复杂程 O(n*logn),
- 平均时间复杂度 O(n*logn)
- 最坏时间复杂度 O(n^2)
堆排序
构造一个大顶堆(根节点元素为最大的元素)
- 最优时间复杂程 O(n*logn),
- 平均时间复杂度 O(n*logn)
- 最坏时间复杂度 O(n*logn)
从零打造 pdqsort
version 1
需要解决的问题
-
短序列的具体长度是多少
给出的建议是12~32,在不同语言和场景中不同,在泛型版本,根据测试选定24
-
如何得知快速排序不佳,以及何时切换到堆排序
当最终选择的分割点的位置离序列两端很接近时,(距离小于 length/8),判断其表现不佳,当这种情况达到一定次数时,切换到堆排序
version 2
优化快排的 pivot
- 短序列(<=8) 选择固定元素即可
- 中序列 (<=50)采样三个元素,选取其中的中位数
- 长序列,采样九个元素,选取中位数
Pivot 的采样方式使得我们有探知序列当前状态的能力
- 如果采用数为逆序,则反转序列
- 如果采样数为正序,则使用 有限制次数的 插入排序
final version
对重复元素较多的情况进行优化
问题?
如何判断序列的重复元素很多呢?
如果两次生成的 pivot 是相同的,则认为进行了无效分割,则认为 pivot 就是重复元素
就将 pivot 相同的值排列到一起
final version 版本也就是 go 1.19默认的排序算法
总结
通过这个课程可以了解到,工程实践过程中和学校课程中的数据结构和算法的区别。没有完美的算法,在实际生产环境中,单一的算法更不可能胜任所有的场景,我们可以取长补短,在不同的场景下,发挥算法的最大优势。