从零开始刷算法——字串与区间类经典题:前缀和 + 单调队列双杀

本篇文章精选两道 LeetCode 高频题:

  • 560. 和为 K 的子数组

  • 239. 滑动窗口最大值

分别代表:

✔ 前缀和 + 哈希表

✔ 双端队列 + 单调队列

这些都是处理 连续区间 / 字串类问题核心技巧,非常适合面试冲刺与能力提升。


一、和为 K 的子数组(前缀和 + 哈希)

题目要求:找到连续子数组,使得它们的和为 k,并返回数量。


解题思路

看到"连续子数组 " + " " → 第一个就要想到 前缀和 PrefixSum

定义前缀和:

cpp 复制代码
s[i] 表示前 i 个元素的和
s[0] = 0 (空数组的和)
s[1] = nums[0] (空数组的和)
s[2] = nums[0] + nums[1]
.....

那么子数组 nums[j..i-1] 的和:

cpp 复制代码
s[i] - s[j] == k  
→ s[i] - k == s[j]

即:
当前前缀和 s[i] 只要能在历史里找到 s[i] - k 的数量,就是答案增加的数量

为了速度:

✔ 用 unordered_map 存储每一种前缀和出现次数

✔ 边遍历边统计


代码实现

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 思路: 看到和就想到前缀和,这时候问题就变成了找s[i] - s[j] = k,所以就可以用hashmap了,那注意点就是前缀和的时候s[i + 1] 来表示前i个数的和。
        int ans = 0;
        vector<int> s(nums.size() + 1);
        for (int i = 0; i < nums.size(); ++i) {
        s[i + 1] = s[i] + nums[i];   // 这里的s[i + 1] 指代的是下标一直到i的和,目的方便计算
        }
        unordered_map<int, int> mp;
        for (int i = 0; i < s.size(); ++i) {
            ans += mp.count(s[i] - k)? mp[s[i] - k] : 0;
            mp[s[i]]++;
        }
        return ans;
    

    }
};

技巧总结

知识点 解释
前缀和 s[0]=0 用来处理从 index=0 开始的子数组
mp 存 prefixSum 次数 避免双循环,时间优化为 O(n)
s[i+1] 定义方式 表示前 i 个元素

二、滑动窗口最大值(单调队列)

维护滑动窗口中最大值

要求 O(n) 时间完成


解题思路

使用 双端队列 deque

但队列内放 下标 + 保持 单调递减(存储的值从头大到尾)

滑动时分 3 步:

步骤 动作
进入窗口 新元素入队前,把小于它的都删掉(维护单调性)
离开窗口 队头元素如果滑出窗口则踢掉(维护最多三个元素)
记录答案 队头就是窗口最大值(因为是单调的,左边就是答案)

飞机看山峰的形象比喻

想象你在飞机上:

  • 山峰(数字)不断进入视野

  • 小山峰被大山峰遮挡(队列维护递减)

  • 离开视野的山峰要从队头弹出

  • 每一刻最高峰就是答案

这样理解非常丝滑!


代码实现

cpp 复制代码
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        // 思路: 想象自己正在坐飞机看外面的山峰,双向队列来记录,同时维护单调性(单调递减)
        vector<int> ans;
        deque<int> q;
        for (int i = 0; i < nums.size(); ++i) {
            // 1.进入
            while (!q.empty() && nums[i] > nums[q.back()]) {
                q.pop_back();
            }
            // 注意这里放下标, 方便后面的判断山峰是否离开视野。
            q.push_back(i);
            // 2.出, 来维护队列只有三个元素
            int left = i - k + 1;
            if (q.front() < left) {  // 注意这里不能用size作比较,因为它维护了单调性删掉了一部分,所以deque不是所有的元素
                q.pop_front();
            }
            // 3.记录答案
            if (left >= 0) {
                ans.push_back(nums[q.front()]);
            }
        }
        return ans;
        
    }
};

总结表

方法 核心思想 时间复杂度 空间复杂度
前缀和 + 哈希 s[i] - s[j] = k O(n) O(n)
单调队列 O(1) 得到窗口最大 O(n) O(k)

文章总结

题型方向 推荐套路
连续子数组求和 前缀和 + Hash
窗口最值统计 单调队列

把这两招吃透,将极大提升字符串和滑动窗口题处理能力

相关推荐
比昨天多敲两行19 小时前
C++ Lsit
开发语言·c++·算法
我爱C编程19 小时前
基于OMP正交匹配追踪和稀疏字典构造的杂波谱恢复算法matlab仿真
算法·matlab·omp·正交匹配追踪·稀疏字典构造·杂波谱恢复
云青黛19 小时前
ReAct(推理与行动)框架
python·算法
im_AMBER19 小时前
Leetcode 142 将有序数组转换为二叉搜索树 | 排序链表
算法·leetcode
码农三叔19 小时前
(10-5-01)大模型时代的人形机器人感知:基于RoboBrain大模型的人形机器人通用智能感知系统(1)构建模型
人工智能·算法·机器人·人形机器人
scott19851219 小时前
扩散模型之(十三)条件生成 Conditioned Generation
人工智能·算法·生成式
Wect20 小时前
LeetCode 53. 最大子数组和:两种高效解法(动态规划+分治)
前端·算法·typescript
春日见20 小时前
端到端自动驾驶综述
linux·人工智能·算法·机器学习·自动驾驶
Book思议-20 小时前
【数据结构实战】单向循环单链表判别条件理解
c语言·数据结构·算法
逆境不可逃20 小时前
【后端新手谈 04】Spring 依赖注入所有方式 + 构造器注入成官方推荐的原因
java·开发语言·spring boot·后端·算法·spring·注入方式