HIT-高级算法 | 六个实验之后,我开始怀疑“快”到底是怎么来的
高级算法实验给我的感觉,是每做完一个题,就会发现“快”并不是一个简单的形容词。集合相似度、选择第 k 小、倒排索引、随机图、集合连接和 Bloom Filter,看起来是六个互不相关的主题,实际上都在逼我思考同一件事:怎样用更聪明的组织方式,少做一些没有必要的工作。
仓库地址:HIT-Advanced-Algorithms-Labs。
实验一:MinHash 和 LSH,把相似集合变成可以快速比较的签名
直接比较两个集合的交集和并集并不难,难的是集合很多、数据很大以后,逐个比较会越来越慢。MinHash 的思路是给集合生成一种压缩签名,用较小的代价估计相似度;LSH 则进一步把可能相似的对象放到相近的桶里,减少候选比较。
我第一次接触这个实验时,最不习惯的是“签名并不等于原数据”。它丢掉了大量细节,却保留了某种相似性信息。算法不是把原问题完整保存下来,而是在精度和速度之间做一个可解释的交换。
实验二:第 k 小元素,排序不是唯一的答案
实验比较了归并排序后取下标、线性选择以及 LazySelect。最直观的做法是先把所有数据排好序,复杂度清楚,也容易写;但如果只需要第 k 小的元素,完整排序可能做了很多不必要的工作。
线性选择通过五个一组的中位数递归选择基准,试图把问题控制在线性复杂度;LazySelect 则先随机抽样,再缩小候选区间。它们让我第一次真正感受到,算法设计经常是在利用题目“只要求一部分信息”这个条件。
实验三:红黑树、跳表和倒排索引,查找结构不能只看平均情况
倒排索引需要根据关键词找到对应内容,红黑树和跳表则提供了不同的有序查找结构。红黑树通过颜色和旋转维护平衡,跳表则用多层链表增加跳跃能力。
我在这里遇到的困难不是不会写插入,而是插入以后结构要一直保持可用。红黑树的颜色约束和旋转一旦处理错,错误可能不会立刻暴露,直到后面某次查找才突然失效。
这让我开始注意数据结构的“不变量”:一个操作结束以后,结构应该继续满足什么条件?比起记住某段旋转代码,知道旋转前后哪些性质必须保持,才更容易排错。
实验四:随机图和 MST,概率问题也可以靠大量实验逼近
随机图实验使用 Kruskal 和抽样,观察最小生成树期望。图的边是随机生成的,单次结果没有固定答案,真正想知道的是很多次实验以后是否呈现出稳定趋势。
这类实验让我再次遇到随机模拟,但这次对象从数字变成了图。并不是每次运行都要得到同一个结果,而是要确认抽样过程、统计方法和实现逻辑没有把趋势扭曲。
实验五:集合连接和数字指纹,先过滤再精确比较
集合包含连接如果直接两两比较,数据规模一大就很慢。实验里尝试使用数字指纹做过滤,把明显不可能匹配的候选先排除,再对剩余对象做精确判断。
这个思路和很多工程系统很像:先用便宜的规则筛掉大多数候选,再把昂贵计算留给少数对象。过滤不能误删真正的答案,所以“快”必须建立在正确性边界之内。
实验六:多重 Bloom Filter,用一点误判换取更快查询
Bloom Filter 不保存完整集合,而是用多个哈希函数设置位图。查询一个元素时,如果某一位没有被设置,可以确定它不在集合;如果所有位置都被设置,只能说它“可能在”。
第一次理解假阳性时,我有点别扭:一个数据结构怎么能把“不确定”当成设计的一部分?后来我发现,在大规模过滤场景里,只要不产生假阴性,用可控的假阳性换取内存和速度,往往是值得的。
六个实验放在一起,我开始怀疑“快”到底是什么
MinHash 用签名减少集合比较;选择算法避免完整排序;红黑树和跳表维护高效查找;随机图用抽样逼近期望;集合连接先过滤再精确比较;Bloom Filter 则接受有限假阳性。
它们没有一个统一的数据结构,却有很强的共同气质:不把所有工作都做满。只要题目允许,只要正确性边界说得清楚,就可以用摘要、抽样、分层、剪枝和概率结构,把计算量压下来。
当然,仓库仍然带着课程实验的边界:大型数据没有全部上传,部分脚本依赖课程路径,随机实验没有统一固定种子,也没有包装成一个现代统一的 benchmark 工程。可正是这些不完美,让我能看到自己当时是如何从“先写出来”,慢慢走向“先想清楚复杂度和不变量”。
仓库地址:HIT-Advanced-Algorithms-Labs。
说明:本文根据个人历史课程实验和公开仓库整理,部分文字经过 AI 辅助润色;报告中的运行数据属于历史实验记录。课程资料仅用于学习回顾和个人作品整理,不用于当前课程作业或考试。

浙公网安备 33010602011771号