算法日记:平衡子数组问题(LeetCode525)
算法日记:从暴力到 O(n) 的线性飞跃 —— 平衡子数组问题(leetcode525)
1. 直觉:暴力 (O(n^2))
最开始拿到这道题,最直观的反应是:枚举所有的子数组。
- 做法:双层
for循环,外层固定起点 i,内层枚举终点 j。 - 判断:统计 [i, j] 区间内 0 和 1 的个数是否相等。
- 瓶颈:随着 n 的增加,计算量呈平方级增长。当 n = 10^5 时,明显超出题目规定范围。
2. 数学建模:前缀和的引入 (O(n^2))
为了优化掉内层的重复计数,我引入了前缀和。
转换思维:把 0 看作
-1,把 1 看作1。核心结论:如果一个子数组 $[i, j]$ 满足 0 和 1 的个数相等,那么它的区间和必为 0。
前缀和公式:
$$
\text{preSum}[j + 1] - \text{preSum}[i] = 0
$$代码实现:
// 第一版逻辑:前缀和 + 暴力查找 |
- 发现问题:虽然计算区间和变快了,但我依然在用双重循环去”寻找”那两个相等的坐标。复杂度依然是 O(n^2)。
3. 优化:哈希表的”位置索引”(O(n))
这是最关键的突破点。我意识到我不需要去”找” j,而是在路过 i 的时候,让哈希表帮我记住它。
逻辑反转:
- 过去:固定左端点,找右端点。
- 现在:站在右端点,回首望向最远的左端点。
核心策略:
$$
如果 \text{preSum}[i] == \text{preSum}[j+1] 说明中间这一段抵消了。
$$- 为了让子数组最长,我只需要记录每个前缀和数值 第一次 出现的位置。
- 哈希表
first[val]就像是一个备忘录:“嘿,海拔为val的地方,我最早在下标i见过你。”
4. 完整代码
class Solution { |
5. 算法心得总结
- 前缀和:将”区间问题”降维成”点与点的关系”。
- 哈希表:将”寻找关系”的时间从 O(n) 降低到 O(1)。
- 这种组合技的套路:
- 看到区间求和,先想前缀和。
- 看到 O(n^2) 找坐标关系,必想哈希表。
- 别忘了初始化(比如
preSum[0] = 0对应的下标是0)。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Chippandaの技术小站!
