跳到主要内容

经典算法

经典算法是程序设计中反复出现的问题解决套路。它们通常可以迁移到很多场景,例如搜索、推荐、缓存、调度、路径规划、数据去重和性能优化。

一、复杂度分析

复杂度用于描述算法随着输入规模增长时的成本变化。

复杂度含义例子
O(1)常数时间数组下标访问
O(log n)对数时间二分查找
O(n)线性时间遍历数组
O(n log n)线性对数快速排序、归并排序
O(n²)平方时间双重循环比较

分析算法时同时关注:

  • 时间复杂度;
  • 空间复杂度;
  • 最坏情况;
  • 平均情况;
  • 输入数据特征。

二、排序算法

常见排序:

算法平均复杂度特点
冒泡排序O(n²)简单,效率低
插入排序O(n²)小规模或基本有序时较好
选择排序O(n²)交换次数少
快速排序O(n log n)实战常用,最坏 O(n²)
归并排序O(n log n)稳定,需要额外空间
堆排序O(n log n)原地排序,不稳定

三、查找算法

常见查找:

  • 顺序查找:适合无序小数据。
  • 二分查找:适合有序数组。
  • 哈希查找:平均 O(1),依赖哈希函数。
  • 树查找:适合动态有序数据。

二分查找重点是边界:

left <= right
mid = left + (right - left) / 2
根据条件移动 left 或 right

四、递归和分治

递归适合把问题拆成相同结构的子问题。

分治的典型流程:

典型算法:

  • 归并排序;
  • 快速排序;
  • 二分查找;
  • 大整数乘法;
  • 最近点对。

五、回溯

回溯适合搜索所有可能解。

通用结构:

适合:

  • 全排列;
  • 子集;
  • 组合;
  • N 皇后;
  • 数独。

六、贪心

贪心每一步都选择当前看起来最优的方案。

适合贪心的问题必须满足:

  • 局部最优能推出全局最优;
  • 子问题之间结构清晰;
  • 选择后不会破坏后续最优性。

典型场景:

  • 区间调度;
  • 找零钱部分问题;
  • Huffman 编码;
  • 最小生成树。

七、动态规划

动态规划适合有重叠子问题和最优子结构的问题。

关键步骤:

  1. 定义状态。
  2. 写出状态转移方程。
  3. 确定初始值。
  4. 确定遍历顺序。
  5. 推导结果。

常见问题:

  • 背包问题;
  • 最长递增子序列;
  • 最长公共子序列;
  • 编辑距离;
  • 爬楼梯;
  • 股票买卖。

八、布隆过滤器

布隆过滤器是一种空间效率很高的概率型数据结构,用于判断一个元素是否可能存在。

特点:

  • 判断不存在一定准确;
  • 判断存在可能误判;
  • 不能直接删除普通元素;
  • 适合大规模去重和缓存穿透防护。

典型场景:

  • 判断 URL 是否爬取过;
  • 防止缓存穿透;
  • 大规模黑名单判断;
  • 日志去重。

总结

经典算法学习路线: