C++滑动窗口技术深度解析:核心原理、高效实现与高阶应用实践

news/2025/2/5 22:01:12 标签: 算法, 数据结构, c++, 开发语言, c语言

目录

一、滑动窗口的核心原理

二、滑动窗口的两种类型

1. 固定大小的窗口

2. 可变大小的窗口

三、实现细节与关键点

1. 窗口的初始化

2. 窗口的移动策略

3. 结果的更新时机

四、经典问题与代码示例

示例 1:和 ≥ target 的最短子数组(可变窗口)

示例 2:无重复字符的最长子串(哈希表辅助)

五、边界条件与易错点

1. 数组越界

2. 初始值的设置

3. 哈希表的使用

4. 循环条件错误

六、时间复杂度分析

七、滑动窗口的适用场景

八、滑动窗口的扩展

1. 结合前缀和

2. 结合单调队列

总结


一、滑动窗口的核心原理

        滑动窗口(Sliding Window) 是一种基于 双指针(Two Pointers) 的算法设计范式,核心思想是通过维护一个 动态窗口区间[left, right]),在遍历过程中调整窗口的左右边界,避免重复计算,从而将时间复杂度优化到 O(n)

  • 核心逻辑

    • 窗口扩张right 指针向右移动,扩大窗口,直到满足某个条件。

    • 窗口收缩:一旦满足条件,left 指针向右移动,缩小窗口,直到不满足条件。

    • 更新结果:在窗口调整过程中,记录最优解。


二、滑动窗口的两种类型

1. 固定大小的窗口
  • 特点:窗口长度固定为 k,通过滑动窗口的起始位置遍历所有可能的子区间。

  • 典型问题

    • 求长度为 k 的子数组的最大平均值

    • 长度为 k 的子字符串的排列匹配

C++ 代码模板

int fixedWindow(vector<int>& nums, int k) 
{
    int sum = 0, max_sum = 0;
    // 初始化窗口
    for (int i = 0; i < k; ++i) sum += nums[i];
    max_sum = sum;
    // 滑动窗口
    for (int right = k; right < nums.size(); ++right) 
    {
        sum += nums[right] - nums[right - k]; // 窗口右移,更新和
        max_sum = max(max_sum, sum);
    }
    return max_sum;
}
2. 可变大小的窗口
  • 特点:窗口大小不固定,根据问题条件动态调整 left 和 right 指针。

  • 典型问题

    • 无重复字符的最长子字符串

    • 和大于等于 target 的最短子数组

C++ 代码模板

int variableWindow(string s) 
{
    unordered_map<char, int> window;
    int left = 0, max_len = 0;
    for (int right = 0; right < s.size(); ++right) 
    {
        char c = s[right];
        window[c]++; // 窗口扩张
        // 窗口收缩条件:出现重复字符
        while (window[c] > 1) 
        {
            char d = s[left];
            window[d]--; // 移出左边界字符
            left++;
        }
        max_len = max(max_len, right - left + 1); // 更新结果
    }
    return max_len;
}

三、实现细节与关键点

1. 窗口的初始化
  • 初始指针位置:通常 left = right = 0

  • 状态变量:如窗口内的和(sum)、哈希表(记录字符频率)等。

2. 窗口的移动策略
  • 扩张条件:一般通过 for 循环逐步移动 right

  • 收缩条件:在满足特定条件时,通过 while 循环移动 left,直到条件不满足。

3. 结果的更新时机
  • 固定窗口:每次窗口滑动后更新结果。

  • 可变窗口:在窗口收缩后或扩张过程中更新结果。


四、经典问题与代码示例

示例 1:和 ≥ target 的最短子数组(可变窗口)
int minSubArrayLen(int target, vector<int>& nums) 
{
    int left = 0, sum = 0;
    int min_len = INT_MAX;
    for (int right = 0; right < nums.size(); ++right) 
    {
        sum += nums[right]; // 窗口扩张
        while (sum >= target) 
        { 
            // 窗口收缩条件
            min_len = min(min_len, right - left + 1);
            sum -= nums[left++]; // 窗口收缩
        }
    }
    return min_len == INT_MAX ? 0 : min_len;
}
示例 2:无重复字符的最长子串(哈希表辅助)
int lengthOfLongestSubstring(string s) 
{
    unordered_map<char, int> last_pos; // 记录字符最后出现的位置
    int left = 0, max_len = 0;
    for (int right = 0; right < s.size(); ++right) 
    {
        char c = s[right];
        // 若字符 c 已存在且在窗口内,移动 left 到 last_pos[c] + 1
        if (last_pos.count(c) && last_pos[c] >= left) 
        {
            left = last_pos[c] + 1;
        }
        last_pos[c] = right; // 更新字符位置
        max_len = max(max_len, right - left + 1);
    }
    return max_len;
}

五、边界条件与易错点

1. 数组越界
  • 在固定窗口中,需确保 right - k >= 0

  • 在可变窗口中,需检查 left 是否超过 right

2. 初始值的设置
  • 最小值初始化为 INT_MAX,最大值初始化为 INT_MIN

3. 哈希表的使用
  • 在字符频率统计中,需确保哈希表的键存在性(如用 count() 检查)。

4. 循环条件错误
  • 错误:在收缩窗口时使用 if 而非 while,导致窗口未完全收缩。

  • 正确:使用 while 循环确保窗口收缩到条件不满足。


六、时间复杂度分析

  • 固定窗口:O(n),每个元素被访问一次。

  • 可变窗口:O(n),每个元素最多被 left 和 right 各访问一次。


七、滑动窗口的适用场景

  1. 连续子数组/子字符串问题

    • 最短/最长满足条件的子数组

    • 子数组的和/乘积/频率统计

  2. 优化暴力解法

    • 将 O(n²) 的暴力枚举优化为 O(n)

  3. 数据流处理

    • 实时处理数据流中的窗口统计量(如移动平均值)


八、滑动窗口的扩展

1. 结合前缀和
  • 用于处理负数数组或更复杂的条件(如子数组和为 k)。

  • 示例代码:

    int subarraySum(vector<int>& nums, int k) 
    {
        unordered_map<int, int> prefix_sum; // 前缀和 -> 出现次数
        prefix_sum[0] = 1;
        int sum = 0, count = 0;
        for (int num : nums) 
        {
            sum += num;
            if (prefix_sum.count(sum - k)) 
            {
                count += prefix_sum[sum - k];
            }
            prefix_sum[sum]++;
        }
        return count;
    }
2. 结合单调队列
  • 用于维护窗口内的最大值/最小值(如滑动窗口最大值问题)。

  • 示例代码:

    vector<int> maxSlidingWindow(vector<int>& nums, int k) 
    {
        deque<int> dq; // 存储下标,按值单调递减
        vector<int> res;
        for (int i = 0; i < nums.size(); ++i) 
        {
            // 移除超出窗口的元素
            if (!dq.empty() && dq.front() == i - k) dq.pop_front();
            // 维护单调队列
            while (!dq.empty() && nums[i] >= nums[dq.back()]) dq.pop_back();
            dq.push_back(i);
            // 记录窗口最大值
            if (i >= k - 1) res.push_back(nums[dq.front()]);
        }
        return res;
    }

总结

滑动窗口的核心在于 双指针的协同移动 和 窗口状态的动态维护。掌握以下要点:

  1. 明确窗口的 扩张与收缩条件

  2. 合理选择 数据结构(如哈希表、单调队列)维护窗口状态。

  3. 注意 边界条件 和 初始值设置

  4. 灵活结合其他算法(如前缀和、单调队列)解决复杂问题。


http://www.niftyadmin.cn/n/5842428.html

相关文章

kamailio-Core 说明书 版本:Kamailio SIP Server v6.0.x(稳定版)

Core 说明书 版本&#xff1a;Kamailio SIP Server v6.0.x&#xff08;稳定版&#xff09; 概述 本教程收集了 Kamailio 导出的函数和参数 core 添加到配置文件中。 注意&#xff1a;此页面上的参数不按字母顺序排列。 结构 kamailio.cfg 的结构可以看作是三个部分&#xff…

【01-Qt-C++-android】

基于Qt的C实现安卓APP 网盘资料 qt 点击下载:5.14.2版本 通过网盘分享的文件&#xff1a;5.14.2 链接: https://pan.baidu.com/s/1VGY1Ija5U4mm4n4qiR-XDw?pwdjpjw 提取码: jpjwandroid 点击下载: sdk&#xff0c;ndk&#xff0c;jdk sdk&#xff0c;ndk&#xff0c;jdk下…

密码学的数学基础1-素数和RSA加密

数学公式推导是密码学的基础, 故开一个新的课题 – 密码学的数学基础系列 素数 / 质数 质数又称素数。 一个大于1的自然数&#xff0c;除了1和它自身外&#xff0c;不能被其他自然数整除的数叫做质数&#xff1b;否则称为合数&#xff08;规定1既不是质数也不是合数&#xff0…

multisim入门学习设计电路

文章目录 1.软件的安装2.电路基本设计2.1二极管的简介2.2最终的设计效果2.3设计流程介绍 3.如何测试电路 1.软件的安装 我是参考的下面的这个文章&#xff0c;文章的链接放在下面&#xff0c;亲测是有效的&#xff0c;如果是小白的话&#xff0c;可以参考一下&#xff1a; 【…

渗透测试之文件包含漏洞 超详细的文件包含漏洞文章

目录 说明 通常分为两种类型&#xff1a; 本地文件包含 典型的攻击方式1&#xff1a; 影响&#xff1a; 典型的攻击方式2&#xff1a; 包含路径解释&#xff1a; 日志包含漏洞&#xff1a; 操作原理 包含漏洞读取文件 文件包含漏洞远程代码执行漏洞: 远程文件包含…

在Mac mini M4上部署DeepSeek R1本地大模型

在Mac mini M4上部署DeepSeek R1本地大模型 安装ollama 本地部署&#xff0c;我们可以通过Ollama来进行安装 Ollama 官方版&#xff1a;【点击前往】 Web UI 控制端【点击安装】 如何在MacOS上更换Ollama的模型位置 默认安装时&#xff0c;OLLAMA_MODELS 位置在"~/.o…

windows电脑-ubuntu,传输文件

FileZilla是一款免费的工具&#xff0c;是基于 FTP 协议进行文件互传的&#xff0c;在传输过程中我们的ubuntu是作为服务器&#xff0c; FileZilla 工具则是作为客户端。 1.ubuntu安装 FTP服务&#xff1a;sudo apt-get install vsftpd 2.检查 /etc/vsftpd.conf 配置文件&…

DeepSeek-R1:通过强化学习提升大型语言模型推理能力的探索

DeepSeek-R1&#xff1a;通过强化学习提升大型语言模型推理能力的探索 在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;的发展日新月异&#xff0c;其在自然语言处理和生成任务中的表现逐渐接近人类水平。然而&#xff0c;如何进一步提升这些模型的推理能…