返回

如何学习算法

学习的步骤

  1. 这个东西是什么,重点是什么
  2. 为什么需要学这个东西
  3. 怎么学这个东西

数据结构与算法

  1. 是什么
    • 数据结构:一组数据的存储结构
    • 算法:操作数据的一组方法
    • 数据结构为算法服务,算法要作用在特定的数据结构之上
  2. 重点是什么
    • 复杂度分析
    • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
    • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  3. 为什么要学
    • 修炼自己的内功、锻炼自己的逻辑思维能力
    • 成为 top 程序员
  4. 怎么学
    • 边学边练,适度刷题(python、java、c++、js)
    • 多思考、多互动
    • 坚持学会、掌握,练习
    • 反复三遍

衡量算法的优劣 – 复杂度分析

  1. 时间复杂度分析,衡量执行算法消耗的时间
  2. 空间复杂度分析,衡量执行算法消耗的存储空间

为什么要复杂度分析

  1. 与普通的事后统计法相比较
    1. 测试结果非常依赖测试环境
    2. 测试结果受数据规模的影响很大
  2. 需要一种衡量标准来描述算法的优劣,并且跟测试数据、测试环境无关

怎么做复杂度分析

时间复杂度分析

  • 执行算法的时间随数据规模增长的变化趋势
  • T(n) = O(f(n))
    • T(n) 代码的执行时间
    • f(n) 代码的执行次数总和
  • 只关注循环执行次数最多的一段代码
  • 加法法则:总复杂度等于量级最大的那段代码的复杂度
    • T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n), g(n)))
  • 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
    • T(n)=T1(n)*T2(n)=O(f(n))*O(g(n))=O(f(n)*g(n))
  • 常见的时间复杂度
    • 多项式量级
      • O(1),代码中不存在循环、递归语句
      • O(logn)、O(nlogn),对数阶时间复杂度,以步长做循环
      • O(m+n)、O(m*n),两个数据的规模决定时间复杂度
    • 非多项式量级:O(2n) 和 O(n!)

复杂度量级
复杂度量级

空间复杂度分析

  • 算法的存储空间与数据规模之间的增长关系
  • 常见的空间复杂度:O(1)、O(n)、O(n ^ 2)
  • 除了原本的数据存储空间外,算法运行需要额外的存储空间

更全面的复杂度分析

  1. 最好情况时间复杂度(best case time complexity)➡️ 最理想的情况
  2. 最坏情况时间复杂度(worst case time complexity)➡️ 最糟糕的情况
  3. 平均情况时间复杂度(average case time complexity)
    • 加权平均时间复杂度、期望时间复杂度
  4. 均摊时间复杂度(amortized time complexity)
    • 有一定的前后时序关系
    • 大部分情况下时间复杂度都相同,个别情况不同
    • 一般情况下,均摊时间复杂度等于最好情况时间复杂度
    • 是一种特殊的平均时间复杂度

解决方案的思考过程

  1. 定义清楚问题
    • 调研问题
    • 对模糊的需求进行假设,限定要解决的问题的范围
  2. 理解隐藏需求
    • 需求一般可以分为功能性需求和非功能性需求
    • 功能性需求一般来讲是和业务逻辑紧密相关的
    • 非功能性需求包括安全、性能、用户体验等
  3. 尝试用学过的数据结构解决这个问题
    • 尝试对比多种数据结构
    • 如果不能直接使用基本数据结构解决,尝试改造数据结构,可以结合多个数据结构的不同特点,使用多个数据结构尝试解决
  4. 计算时间、空间复杂度,包括对内存、磁盘的访问

合理地选择数据结构和算法

  1. 熟知每种数据结构和算法的功能、特点、时间空间复杂度
  2. 时间、空间复杂度不能跟性能划等号
    • 复杂度不是执行时间和内存消耗的精确值,会忽略低阶、常数、系数
    • 处理小规模数据时,代码的执行时间有时不跟时间复杂度成正比
    • 对于处理不同问题的不同算法,复杂度没有可比性
  3. 算法的选择,一定要根据数据规模来抉择。数据规模小的时候,不必选择高级算法,而是选择简单、易维护、易实现的算法
  4. 解决问题的重点在于对需求的调研、理解,理清楚要处理数据的特征与访问方式
  5. 区别对待 IO 密集、内存密集、计算密集
    • 数据在磁盘上,代码的性能瓶颈可能在磁盘 IO,要尽可能地减少磁盘 IO 的次数
    • 数据在内存中,判断代码是内存密集型还是 CPU 密集型
      • CPU 密集型,CPU 计算耗时占大部分,在选择数据结构和算法的时候,要尽量减少逻辑计算的复杂度,如 用位运算代替加减乘除等
      • 内存密集型,内存数据的读取耗时占大部分,可以考虑是否能减少数据的读取量,数据是否在内存中连续存储,是否能利用 CPU 缓存预读
  6. 使用现有算法,避免重复造轮子
  7. 不要漫无目的地过度优化

学习书单

  1. 《大话数据结构》
  2. 《算法图解》
  3. 《数据结构和算法分析》
  4. 《剑指 offer》
  5. 《编程珠玑》
  6. 《编程之美》
  7. 《算法》
  8. 《算法导论》
  9. 《计算机程序设计艺术》
  10. 《算法帝国》
  11. 《数学之美》
  12. 《算法之美》
© Licensed Under CC BY-NC-SA 4.0