01 为什么要学习数据结构和算法
例子-抖音直播排行榜功能
规则:某个时间段内,直播间礼物数TOP10房间获得奖励,需要在每个房间展示排行榜
解决方案
- 礼物数量存储在 Redis-zset 中,使用 skiplist 使得元素整体有序
- 使用 Redis 集群,避免单机压力过大,使用 主从算法、分片算法
- 保证集群原信息的稳定,使用 一致性算法
- 后端使用 缓存算法(LRU) 降低 Redis 压力,展示房间排行榜
02 经典排序算法
插入排序:最好 O(n) | 最坏 O(n2) | 平均 O(n2)
快速排序:最好 O(nlogn) | 最坏 O(n2) | 平均 O(nlogn)
堆排序:最好 O(nlogn) | 最坏 O(nlogn) | 平均 O(nlogn)
实际场景
根据序列元素排列情况划分
- 完全随机的情况 (random
- 有序/逆序的情况 (sorted/reverse
- 元素重复度较高的情况 (mod8)
- 在此基础上,还需要根据序列长度的划分(16/128/1024
结论
- 所有短序列和元素有序情况下,插入排序性能最好
- 在大部分的情况下,快速排序有较好的综合性能
- 几乎在任何情况下,堆排序的表现都比较稳定
03 从零开始打造pdqsort
pdqsort - version1
结合三种排序方法的优点
- 对于短序列 (小于一定长度)我们使用插入排序
- 其他情况,使用快速排序来保证整体性能
- 当快速排序表现不佳时,使用堆排序来保证最坏情况下时间复杂度仍然为 O(n*logn)
pdqsort - version2
- 升级 pivot 选择策略(近似中位数)
- 发现序列可能逆序,则翻转序列 -> 应对 reverse 场景
- 发现序列可能有序,使用有限插入排序 -> 应对 sorted 场景
pdqsort - version3
优化-重复元素较多的情况(partitionEqual)
- 当检测到此时的 pivot 和上次相同时 (发生在 leftSubArray) ,使用partitionEqual 将重复元素排列在一起,减少重复元素对于 pivot 选择的干扰
优化-当 pivot 选择策略表现不佳时,随机交换元素
- 避免一些极端情况使得 QuickSort 总是表现不佳,以及一些黑客攻击情况
最终版本