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

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

  • 560. 和为 K 的子数组

  • 239. 滑动窗口最大值

分别代表:

✔ 前缀和 + 哈希表

✔ 双端队列 + 单调队列

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


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

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


解题思路

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

定义前缀和:

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

那么子数组 numsj..i-1 的和:

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

即:
当前前缀和 si 只要能在历史里找到 si - 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;
    

    }
};

技巧总结

知识点 解释
前缀和 s0=0 用来处理从 index=0 开始的子数组
mp 存 prefixSum 次数 避免双循环,时间优化为 O(n)
si+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;
        
    }
};

总结表

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

文章总结

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

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

相关推荐
小郎君65813 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
算法
邵奈一14 小时前
GPT-6.1 Terra速度是Sol的3倍,价格减半
算法
RaaS10014 小时前
Node.js Brotli RCE 漏洞自查指南:fetch、undici、axios 都在影响面,三分钟确认你的版本
人工智能·深度学习·算法
shehuiyuelaiyuehao14 小时前
算法56,重排链表
数据结构·算法·链表
qq_4523962315 小时前
第四篇:《索引原理与实战:B-tree、Hash、GIN、GiST、BRIN》
算法·哈希算法·gin
西索斯coding15 小时前
Qwen3-Max 与 Qwen-Plus:60 个真实修复任务评测方案
大数据·网络·算法·ai
INGNIGHT16 小时前
440 · 背包问题(三)(无限次数)
算法·leetcode·动态规划
suhan16 小时前
【数据结构】七、查找:1.查找的概念、线性结构查找(顺序、折半(二分)、插值、稠密、分块、倒排)
数据结构·算法·排序算法
killerbasd16 小时前
总结 10.26
算法