数据结构与算法 | 青训营笔记

102 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第 4 篇笔记。

本篇笔记是对《数据结构与算法》课程的知识点总结,主要内容是生产环境使用的算法和数据结构。

课前问题

例子:抖音直播排行榜 规则:某个时间段内,直播间礼物 top10 房间获得奖励,在每个房间展示排行榜。

解决方案:

  • 礼物数量存储在 Redis-zset 中,使用 skiplist 使得元素整体有序
  • 使用 Redis 集群,避免单机压力过大,使用主从算法、分片算法
  • 保证集群原信息的稳定,使用一致性算法
  • 后端使用缓存算法(LRU)降低 Redis 压力,展示房间排行榜

go(<=1.18)introsort go(>1.18)pbqsort

经典排序算法

插入排序

将后面的元素不断插入已经排序好的 array 中

快速排序

分治思想,不断的分割序列直至整体有序

  • 选定一个 pivot
  • 使用 pivot 分割序列,将元素分成比 pivot 大的和比 pivot 小的两个序列

堆排序

利用堆的性质形成的排序算法

  • 构造大顶堆
  • 将根结点(最大元素)交换到最后一个位置,调整整个堆

实际场景 benchmark 时间复杂度

bestavgworst
插入排序O(n)O(n^2)O(n^2)
快速排序O(nlogn)O(nlogn)O(n^2)
堆排序O(nlogn)O(nlogn)O(nlogn)

根据序列元素排列情况划分:

  • 完全随机的情况(random)
  • 有序/逆序的情况(sorted/reverse)
  • 元素重复度较高的情况(mod8)

在此基础上,根据序列长度划分(16/128/1024):

  • 短序列(len<=16):插入排序
  • 中、长序列:快速排序
  • 堆排序速度与最快算法差距不大
  • 序列本身有序的情况下:插入排序

结论:

  • 所有短序列和元素有序情况下,插入排序性能最好
  • 在大部分的情况下,快速排序有较好的综合性能
  • 几乎在任何情况下,堆排序的表现都比较稳定

如何设计更好的算法?

bestavgworst
插入排序O(n)O(n^2)O(n^2)
快速排序O(nlogn)O(nlogn)O(n^2)
堆排序O(nlogn)O(nlogn)O(nlogn)pdqsortO(n)O(nlogn)O(nlogn)

pdqsort

pdqsort(pattern-defeating-quicksort) 是一种不稳定的混合排序算法,它的不同版本被应用在 C++ BOOST、Rust 以及 Go 1.19 中。它对常见的序列类型做了特殊的优化,使得在不同条件下都拥有不错的性能

version1: 结合三种排序的优点

  • 短序列(len=24)使用 insertsort
  • 其他情况,用 quicksort(选择首个元素作为 pivot) 保证整体性能
  • 当 quicksort 表现不佳(limit==0)时,用 heapsort 保证最坏情况下的时间复杂度仍为 O(nlogn)

Q:如何得知表现不佳,以及何时切换到堆排序? A:当最终 pivot 的位置离序列两端很近时(d<length/8)判断为表现不佳;当这种情况的发生次数到的 limit(即 bits.Len(lenght)),切换到堆排序。

如何让pdqsort速度更快?

  • 尽量使得 QuickSort 的 pivot 为序列的中位数->改进 choose pivot
  • Partition 速度更快->改进 partition ,但是此优化在Go表现不好

version2:优化Pivot的选择

升级 pivot 选择策略,近似中位数。几次选择的中位数如果是降序的,那序列可能是逆序的,翻转序列;如果是可能升序的,使用插入排序

Pivot 的选择:根据序列长度不同,确定选择策略

  • 短序列(<=8),选择固定元素
  • 中序列(<=50),采样三个元素,median of three
  • 长序列(>50),采样九个元素,median of medians

采样的元素都是逆序 → 序列可能逆序 → 翻转序列; 采样的元素都是顺序 → 序列可能有序 → 插入排序(partial insertion sort,即有限制次数的插入排序)

final version:

重复元素较多的情况(partitionEqual): 当检测到此时的 pivot 和上次相同时(发生在 leftSubArray),使用partitionEqual将重复元素排列在一起,减少重复元素对于pivot选择的干扰

当pivot选择策略表现不佳时,随机交换元素: 避免一些极端情况使得QuickSort总是表现不佳,以及一些黑客攻击情况