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

本篇文章精选两道 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
窗口最值统计 单调队列

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

相关推荐
CoderCodingNo14 分钟前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
大闲在人25 分钟前
7. 供应链与制造过程术语:“周期时间”
算法·供应链管理·智能制造·工业工程
小熳芋28 分钟前
443. 压缩字符串-python-双指针
算法
Charlie_lll38 分钟前
力扣解题-移动零
后端·算法·leetcode
chaser&upper38 分钟前
矩阵革命:在 AtomGit 解码 CANN ops-nn 如何构建 AIGC 的“线性基石”
程序人生·算法
weixin_499771551 小时前
C++中的组合模式
开发语言·c++·算法
iAkuya1 小时前
(leetcode)力扣100 62N皇后问题 (普通回溯(使用set存储),位运算回溯)
算法·leetcode·职场和发展
近津薪荼1 小时前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
xiaoye-duck1 小时前
吃透 C++ STL list:从基础使用到特性对比,解锁链表容器高效用法
c++·算法·stl
松☆1 小时前
CANN与大模型推理:在边缘端高效运行7B参数语言模型的实践指南
人工智能·算法·语言模型