讲解
算法是解决问题的明确步骤,数据结构是组织数据的方式。两者是一体两面:选择了不同的数据结构,可行的算法就不同;反过来,算法的目标也决定了该用什么结构来承载数据。比如「在一百万个数里查一个值是否存在」,用数组逐个扫描要一百万次比较,用哈希表平均只要一次——同一个问题,结构不同,效率差出六个数量级。
为什么要专门学算法?最现实的回答是面试:国内外技术面试普遍以算法题为载体,考察的是你把模糊问题转化为精确步骤的能力。但更长期的价值在于工程判断力——评估一个方案能不能扛住数据量、在两种实现之间做取舍、读懂开源代码里的巧妙设计,这些都依赖算法功底。算法不会直接帮你写业务代码,但它决定了你能多快看出业务代码里的性能隐患。
学习算法最容易陷入的误区是「背题」。题目是无穷的,但模式是有限的:双指针、滑动窗口、二分、回溯、动态规划……本教程按模式而不是按题号组织,每一章讲透一种模式的思想内核,再用几道经典题目演示它如何落地。掌握了模式,新题只是旧模式的换装。
本教程全部示例使用 JavaScript(Node.js 环境),原因很简单:语法噪音少,能让算法本身站在舞台中央;而且对前端和 Node 开发者来说零环境成本。需要强调:每一章的每一段代码都不是摆设——它们在本教程的构建流程中被真实执行,断言全部通过才会发布。你看到的每一个输出,都是程序跑出来的结果。
学习建议是动手大于阅读:每章的示例先自己实现一遍,再看参考实现,最后试着把断言里的测试用例改掉,验证你的实现是否真的健壮。刷题平台(LeetCode 等)上的题号会在讲解中提及,方便你按图索骥去练习。
示例
先用一个最小的例子感受「算法选择」的力量:计算 1 加到 n。循环累加是 O(n),高斯求和公式是 O(1)——n 越大,差距越夸张。下面这段代码在本教程构建时被真实执行:
import assert from 'node:assert/strict';
function sumLoop(n) {
let total = 0;
for (let i = 1; i <= n; i++) total += i;
return total;
}
function sumFormula(n) {
return (n * (n + 1)) / 2;
}
assert.strictEqual(sumLoop(100), 5050);
assert.strictEqual(sumFormula(100), 5050);
assert.strictEqual(sumLoop(1_000_000), sumFormula(1_000_000));
console.log('循环累加 1..100 =', sumLoop(100));
console.log('高斯公式 1..100 =', sumFormula(100));
console.log('两种方法在 n=1000000 时结果一致');
两个函数结果相同,但成本完全不同:sumLoop 的时间随 n 线性增长,sumFormula 无论 n 多大都是一次乘加。这就是「换个算法」最朴素的形态。本教程会反复看到这样的时刻——很多时候,优化不是把循环写得更快,而是把循环消灭掉。
常见坑
- 只看不动手:看懂了和自己能写出来之间隔着十次亲手实现。每章代码都短,务必敲一遍。
- 一开始追求最优解:先写出正确的暴力解,再谈优化。面试中「先暴力再优化」反而是加分项。
- 背题不背模式:题目做不完,模式学得完。遇到新题先问「它属于哪个模式」。
- 忽视边界用例:空数组、单元素、全部相同——大多数错误实现在边界上露馅,写断言时先写边界。
- 纠结语言选择:算法思想与语言无关,用你最顺手的语言学,不要中途换语言重学。
小结
算法 = 解决问题的步骤,数据结构 = 组织数据的方式;学算法要学模式而不是背题目。本教程 24 章,所有代码经 Node.js 真实执行验证。下一章先学会衡量算法好坏的标尺:时间复杂度与空间复杂度。