前缀和算法如何用一次预处理换来O(1)区间查询

给定一个长度为100万的数组,需要回答10万次查询——每次查询要求计算某个区间的元素之和。最直观的做法是:对每个查询,遍历从左端点到右端点的所有元素进行累加。假设平均区间长度为50万,总操作次数约为500亿次,在现代CPU上需要运行数秒甚至更久。 ...

9 min · 4439 words

滑动窗口算法:从暴力枚举到线性扫描的效率革命

在一个长度为 100 万的数组中找出连续 k 个元素的最大和。最直观的思路是:对每个可能的起始位置,累加 k 个元素,记录最大值。这需要大约 100 万次循环,每次循环内部还要遍历 k 个元素——总的时间复杂度是 $O(n \times k)$。 ...

11 min · 5044 words

二分查找算法:从十个程序员九个错到LeetCode通关的完整指南

Jon Bentley 在他的经典著作《Programming Pearls》中记录了一个令人震惊的实验结果:在贝尔实验室和 IBM 的课程中,他给专业程序员几个小时的时间实现二分查找,结果超过 90% 的人写出了有 bug 的代码。更令人惊讶的是,二分查找算法首次发表于 1946 年,但第一个正确的实现直到 1962 年才出现——整整 16 年间,这个看似简单的算法一直以"错误"的形式流传。 ...

10 min · 4518 words

双指针算法:从两次遍历到一次扫描的优雅降维

假设你面前有一组从小到大排好序的数字,需要找出其中任意两个数之和等于目标值的组合。最直观的想法是嵌套两层循环,逐一尝试所有配对——时间复杂度 $O(n^2)$,当数据量达到十万级别时,程序开始变得迟缓。 ...

8 min · 3922 words

动态规划算法入门:从暴力递归到状态转移的完整思维指南

当你第一次尝试实现斐波那契数列时,可能会写出这样的递归代码: public int fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } 这段代码看起来简洁优雅,但当 n 达到 50 时,程序会运行很长时间才能返回结果。问题出在哪里?重复计算。计算 fib(5) 时会调用 fib(3),计算 fib(4) 时又会再次调用 fib(3)。随着 n 增大,重复计算的次数呈指数级增长——时间复杂度高达 $O(2^n)$。 ...

12 min · 5958 words

为什么主流语言的sort函数都选择了Timsort

1959年,Tony Hoare发明了快速排序。这个算法在教科书中被奉为圭臬,平均时间复杂度$O(n \log n)$,原地排序,缓存友好,几乎完美。然而,六十多年后的今天,当你调用Python的list.sort()、Java的Arrays.sort()或Rust的稳定排序时,底层运行的却是一个叫Timsort的算法。 ...

10 min · 4652 words

错误处理为何分裂五十年:从PL/I的ON语句到Rust的Result类型

1964年,IBM开始设计PL/I语言,并引入了一个名为ON的语句。程序员可以用ON ENDFILE(SYSIN) GO TO LABEL;来指定当文件结束时跳转到特定标签。这是编程语言史上第一次将"异常情况处理"作为一等语言特性。 ...

12 min · 5800 words