大家知道,计算机专业的基础课程之一就是数据结构和算法,最近有机会再次研读了 《秒懂算法:用常识解读数据结构与算法》 (A Common-Sense Guide to Data Structures and Algorithms ),这本书不像传统那种干巴巴讲数据结构的课程,其语言风格和示例都非常简洁明了,其核心特色是去数学化,通过大量直观的图解和生活中的常识来解释复杂的计算机算法逻辑,值得大家有空翻翻并回顾一下计软件设计开发的基础。以下是该书的主要内容和核心知识点整理,供大家参考。
1. 核心衡量标准:大 O 表示法 (Big O Notation)
用于衡量算法效率的指标主要有时间复杂度和空间复杂度,主要关注时间复杂度,即算法需要的步骤数,书中首先确立了评价代码效率的统一语言。
- 时间复杂度 :衡量算法运行步数随数据量增长的变化趋势。
- :常数时间,效率最高,O(1)。
- :对数时间,如二分查找,O(log(n))。
- :线性时间,如线性查找,O(n)。
- :快速排序等高效排序算法,O(n log(n))。
- :嵌套循环,如冒泡排序、选择排序、插入排序等,O(n2)。
- 空间复杂度:衡量算法运行过程中额外消耗的存储空间。
2. 基础数据结构
数据结构是数据的组合方式,同样的数据可以有多种组合方式,不同的组织方式,极大地影响代码运行速度,甚至相差几个数量级。书中对比了不同数据结构在读取、查找、插入、删除这四种操作对比:

3. 经典算法分类
算法就是完成特定任务所需的一组操作。书中通过对比展示了常见经典算法的优化过程,具体如下,排序是为了后续查找使用。
- 排序算法 :
- 冒泡/选择/插入排序:基础排序,复杂度通常为 O(n2)。
- 快速排序 (Quicksort):基于分治思想,平均复杂度为O(n log(n)),是实践中最常用的排序。
- 查找算法 :
- 线性查找:逐个比对。
- 二分查找:针对有序数组,每次排除一半数据。
- 递归 (Recursion) :
- 核心:基准情形 (Base Case) + 递归步骤。
- 风险:栈溢出。
- 优化:通过动态规划 (Dynamic Programming) 或 记忆化 (Memoization) 解决重复计算。
4. 进阶数据结构
在基础数据结构的基础上引申出多种高级数据结构,常见的如下:
- 二叉树 (Binary Tree) :
- 二叉搜索树 (BST):左小右大,查找、插入、删除均为O(log(n)) 。
- 堆 (Heap) :
- 始终能快速找到最大或最小值,常用于优先级队列。
- 图 (Graph) :
- 用于模拟复杂关系(社交网络、地图)。
- 广度优先搜索 (BFS):找最短路径。
- 深度优先搜索 (DFS):探索所有路径。
- 前缀树 (Trie) :
- 专门用于字符串处理,如自动补全功能。
5. 核心实战建议
书中不仅讲理论,还提供了提升代码质量的实用建议:
- 代码优化:不要只关注代码行数,更要关注大 O 复杂度。
- 选择合适的数据结构:这是算法优化的第一步。例如,将数组换成散列表,往往能将算法复杂度降低 。
- 权衡 (Trade-offs):有时候需要用空间换时间(常见办法),或者用时间换空间,比如分片存储加载,提前算好月度销售额,这样在计算年度销售额时自动使用月度数据。
6. 典型示例列举
这两个示例和这些观点构成了全书的灵魂:用直觉理解逻辑,用逻辑优化性能。
示例一:电话簿查找(解释二分查找 )
这是书中解释算法效率最经典的例子:
- 场景:如果你要在一部厚厚的电话簿里找一个名字(比如 "张")。
- 线性查找(普通人思维):从第一页开始往后翻,直到找到为止。如果名字在最后,你要翻几千次,这就是线性时间复杂度 。
- 二分查找(算法思维):从电话簿的正中间翻开。如果中间的名字是 "M",你知道 "Z" 肯定在后半部分,于是直接撕掉前半部分。重复这个过程,每次都排除掉一半。
- 结论 :即使电话簿增加一倍(从 1000 页变 2000 页),二分查找也仅仅是多增加 1 步。这直观地展示了为什么 的效率远高于 。
示例二:超市购物清单(解释数组与集合的性能差异)
书中通过这个例子解释了为什么"看似相同"的操作,性能却大相径庭:
- 场景:你需要记录购物清单。
- 数组 (Array):你直接把商品写在清单末尾。这很快,步数是1。
- 集合 (Set):集合要求不能有重复项。所以每次你想写下"苹果"时,必须先从头到尾检查一遍清单里是否已经有了"苹果"。
- 结论 :在数组末尾插入是 1,但在集合中插入是 O(n)。这个例子告诉读者:为了维持某种数据特征(如唯一性),必须付出性能代价。
7. 核心观点
- 数据结构决定算法效率:算法不是凭空存在的,它依赖于数据的组织方式(数据结构)。选择正确的数据结构,往往能让复杂的算法问题迎刃而解。
- 关注"步数"而非"秒数":衡量代码快慢的标准不应该是运行了多少秒(因为硬件不同),而应该是随着数据量 的增长,操作步数是如何增长的。
- 最坏情况原则:大 O 表示法通常关注"最坏情况"。务实程序员会为最坏的情况做准备,因为那是性能瓶颈所在。
- 空间与时间的权衡 (Trade-off):有时候为了让程序跑得更快,我们需要消耗更多的内存;反之亦然。没有完美的算法,只有最适合当前场景的算法。
8. 书中金句
- 大 O 表示法 : "大 O 表示法并不告诉你代码运行的具体秒数,它告诉你的是:当数据量增加时,你的代码会变慢到什么程度。"
- 数据结构的重要性 : "数据结构不仅是存储数据的方式,它们还是影响程序性能的'形状'。"
- 代码优化 : "在盲目优化代码之前,先看看你的大 O 复杂度。把一个 的算法改成 ,比任何微小的代码调整都管用。"
- 关于递归 : "递归是处理复杂问题的优雅方式,但如果你忘了写'基准情形'(Base Case),它就是通往内存崩溃的单程票。"
- 核心哲学 : "理解算法的常识,比死记硬背公式重要得多。"