C++ 滑动窗口

前言

更新(2025-4-27)

新的Springboot+Vue项目重磅推出 ◔.̮◔✧

只需一杯奶茶的价钱就能收获一份完整的前后端分离项目,供课设或二次开发哦~保证项目能启动,需要的私(接定制项目开发)

下面展示几张图片:

部分功能预览:

后台首页: 

 前台首页:

资料:

有需要的宝子欢迎私信哦~(还有其他的类似项目)


 

C++ 中滑动窗口分两种,一种是给定窗口长度,还有一种是不定长窗口长度。

本篇文章主要讲解这两种状态的滑动窗口,结合例题让读者更好的理解

一、给定窗口长度K

一般的,对于给定窗口长度的题,通常要求我们对窗口内的元素进行一些操作,解决一些问题,具体问题具体分析。

给定窗口长度的题目通常要求解决以下问题:

  • 找到每个窗口内的最大值或最小值。

  • 计算每个窗口的和或平均值。

  • 统计满足条件的子数组数量。

  • 找到满足条件的最长或最短子数组。

  • 统计窗口内的唯一元素数量。

可以在 O(n) 的时间复杂度内解决问题

题目1:

力扣 1343 题大小为 K 且平均值大于等于阈值的子数组数目

给你一个整数数组 arr 和两个整数 kthreshold

请你返回长度为 k 且平均值大于等于 threshold 的子数组数目。

示例 1:

输入:arr = [2,2,2,2,5,5,5,8], k = 3, threshold = 4
输出:3
解释:子数组 [2,5,5],[5,5,5] 和 [5,5,8] 的平均值分别为 4,5 和 6 。其他长度为 3 的子数组的平均值都小于 4 (threshold 的值)。

示例 2:

输入:arr = [11,13,17,23,29,31,7,5,2,3], k = 3, threshold = 5
输出:6
解释:前 6 个长度为 3 的子数组平均值都大于 5 。注意平均值不是整数

直接上代码:

class Solution {
public:
    int numOfSubarrays(vector<int>& arr, int k, int threshold) {
        int ans = 0;
        int s = 0; // 维护窗口元素和
        for (int i = 0; i < arr.size(); i++) {
            // 1. 进入窗口
            s += arr[i];
            
            if (i < k - 1) { // 窗口大小不足 k
                continue;
            }
            // 2. 更新答案
            ans += s >= k * threshold;
            // 3. 离开窗口
            s -= arr[i - k + 1];
        }
        return ans;
    }
};

解释:总共主要分3步,不足窗口长度的先进入窗口,满足长度等于窗口大小后更新答案,之后就是窗口往后移,右边元素进入窗口,左边元素移除窗口,然后不断更新答案。

题目2:

力扣2461题长度为 K 子数组中的最大和

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和:

  • 子数组的长度是 k,且
  • 子数组中的所有元素 各不相同 。

返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0 。

子数组 是数组中一段连续非空的元素序列。

示例 1:

输入:nums = [1,5,4,2,9,9,9], k = 3
输出:15
解释:nums 中长度为 3 的子数组是:
- [1,5,4] 满足全部条件,和为 10 。
- [5,4,2] 满足全部条件,和为 11 。
- [4,2,9] 满足全部条件,和为 15 。
- [2,9,9] 不满足全部条件,因为元素 9 出现重复。
- [9,9,9] 不满足全部条件,因为元素 9 出现重复。
因为 15 是满足全部条件的所有子数组中的最大子数组和,所以返回 15 。

示例 2:

输入:nums = [4,4,4], k = 3
输出:0
解释:nums 中长度为 3 的子数组是:
- [4,4,4] 不满足全部条件,因为元素 4 出现重复。
因为不存在满足全部条件的子数组,所以返回 0 。

上代码看注释来理解

class Solution {
public:
    long long maximumSubarraySum(vector<int>& nums, int k) {
        int n = nums.size();  // 获取数组的长度
        long long sum = 0;    // 当前窗口的和
        long long ma = 0;     // 用于存储最大子数组和
        unordered_map<int, int> hash;  // 哈希表,用于记录窗口内每个元素的出现次数

        // 初始化窗口的前 k-1 个元素
        for (int i = 0; i < k - 1; i++) {
            hash[nums[i]]++;  // 更新元素的出现次数
            sum += nums[i];   // 将元素加入当前窗口的和
        }

        // 遍历数组,维护一个大小为 k 的滑动窗口
        for (int i = k - 1; i < n; i++) {
            hash[nums[i]]++;  // 将当前元素加入窗口,并更新其出现次数
            sum += nums[i];   // 更新当前窗口的和

            // 如果窗口内有 k 个不同的元素,更新最大和
            if (hash.size() == k) {
                ma = max(ma, sum);  // 更新最大子数组和
            }

            // 移动窗口:移除窗口左侧的元素
            hash[nums[i - k + 1]]--;  // 减少左侧元素的出现次数
            if (hash[nums[i - k + 1]] == 0) {
                hash.erase(nums[i - k + 1]);  // 如果元素的出现次数为 0,从哈希表中移除
            }
            sum -= nums[i - k + 1];  // 从当前窗口的和中移除左侧元素
        }

        return ma;  // 返回最大子数组和
    }
};

逻辑解释:

  1. 初始化窗口

    • 使用一个哈希表 hash 来记录窗口内每个元素的出现次数。

    • 使用 sum 来记录当前窗口的和。

    • 首先将前 k-1 个元素加入窗口,初始化 sumhash

  2. 滑动窗口遍历

    • 从第 k-1 个元素开始遍历数组。

    • 每次将当前元素 nums[i] 加入窗口,并更新其出现次数和窗口的和。

    • 检查窗口内是否有 k 个不同的元素:

      • 如果有 k 个不同的元素,更新最大和 ma

    • 移动窗口:移除窗口左侧的元素 nums[i-k+1],更新其出现次数和窗口的和。

      • 如果某个元素的出现次数变为 0,从哈希表中移除该元素。

  3. 返回结果

    • 遍历结束后,ma 中存储的就是满足条件的最大子数组和。

二、不定长窗口大小

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,以及求子数组个数

通常我们对于处理串的问题时,使用暴力算法超时的时候,就可以考虑使用滑动窗口来优化时间了

看题目来加深理解

题目1:

力扣904题水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

AC代码

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int n = fruits.size();  // 获取果树的数量
        unordered_map<int, int> hash;  // 哈希表,用于记录当前窗口内每种水果的数量
        int left = 0;  // 滑动窗口的左边界
        int ma = 0;  // 用于记录最多可以采摘的水果数量

        // 遍历所有果树,right 表示滑动窗口的右边界
        for (int right = 0; right < n; right++) {
            hash[fruits[right]]++;  // 将当前果树的水果加入窗口,并更新其数量

            // 如果窗口内有超过两种水果,需要收缩窗口
            while (hash.size() > 2) {
                int out = fruits[left];  // 获取窗口左侧的水果
                hash[out]--;  // 减少左侧水果的数量
                if (hash[out] == 0) {
                    hash.erase(out);  // 如果某种水果的数量变为 0,从哈希表中移除
                }
                left++;  // 移动左边界,缩小窗口
            }

            // 更新最多可以采摘的水果数量
            // 当前窗口的水果数量为 right - left + 1
            ma = max(ma, right - left + 1);
        }

        return ma;  // 返回最多可以采摘的水果数量
    }
};

很多题套路都差不多的,对于不定长滑动窗口大小,用的比较多的就是哈希表来存储

  1. 初始化

    • n:果树的总数。

    • hash:哈希表,用于记录当前窗口内每种水果的数量。

    • left:滑动窗口的左边界,初始值为0。

    • ma:用于记录最多可以采摘的水果数量,初始值为0。

  2. 滑动窗口遍历

    • 使用 right 作为滑动窗口的右边界,从左到右遍历所有果树。

    • 每次将当前果树的水果加入窗口,并更新其在哈希表中的数量。

  3. 收缩窗口

    • 如果当前窗口内有超过两种水果(hash.size() > 2),需要收缩窗口。

    • 移动左边界 left,移除窗口左侧的水果,直到窗口内只剩下两种水果。

  4. 更新结果

    • 每次调整窗口后,计算当前窗口的水果数量(right - left + 1),并更新最大值 ma

  5. 返回结果

    • 遍历结束后,ma 中存储的就是最多可以采摘的水果数量。

题目2:

力扣1695题删除子数组的最大得分

给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组删除子数组的 得分 就是子数组各元素之  。

返回 只删除一个 子数组可获得的 最大得分 。

如果数组 b 是数组 a 的一个连续子序列,即如果它等于 a[l],a[l+1],...,a[r] ,那么它就是 a 的一个子数组。

示例 1:

输入:nums = [4,2,4,5,6]
输出:17
解释:最优子数组是 [2,4,5,6]

示例 2:

输入:nums = [5,2,1,2,5,2,1,2,5]
输出:8
解释:最优子数组是 [5,2,1] 或 [1,2,5]

这题和上题非常相识,都是同一个套路,变的就是题目给的条件

class Solution {
public:
    int maximumUniqueSubarray(vector<int>& nums) {
        int n = nums.size();  // 获取数组的长度
        long long ma = 0;     // 用于存储最大唯一子数组的和
        int left = 0;         // 滑动窗口的左边界
        long long sum = 0;    // 当前窗口内所有元素的和
        unordered_map<int, int> hash;  // 哈希表,用于记录窗口内每个元素的出现次数

        // 遍历数组,right 表示滑动窗口的右边界
        for (int right = 0; right < n; right++) {
            // 将当前元素加入窗口,并更新其出现次数
            hash[nums[right]]++;
            sum += nums[right];  // 更新当前窗口的和

            // 如果当前元素的出现次数大于 1,说明窗口内有重复元素
            // 需要收缩窗口,移除窗口左侧的元素,直到窗口内没有重复元素
            while (hash[nums[right]] > 1) {
                sum -= nums[left];  // 从窗口的和中移除左侧元素
                hash[nums[left]]--;  // 更新左侧元素的出现次数
                left++;  // 移动左边界
            }

            // 更新最大唯一子数组的和
            ma = max(ma, sum);
        }

        return ma;  // 返回最大唯一子数组的和
    }
};

万变不离其宗,只有多刷题才能加深算法的印象。祝各位读者在刷算法的路上越走越远 0v0!

 

 

 

 

 

<think>好的,我现在需要处理用户关于Hadoop面试题的请求。首先,用户明确提到想要查找与Hadoop相关的面试题及答案解析,并提供了三个引用内容。我需要确保回答符合用户的要求,同时正确引用提供的资料。 首先,我要回顾用户提供的引用内容: 引用[1]指出Hadoop不支持随机读写,而适用于顺序访问。引用[2]详细介绍了Hadoop的基本概念,特别是HDFS的特点,如分布式存储、容错机制,以及“一次写入、多次读取”的模式。引用[3]列出了Hadoop的组件,如NameNode、DataNode等。 接下来,我需要生成相关的面试题,确保问题覆盖Hadoop的核心概念,并利用提供的引用内容。例如,关于HDFS的随机读写问题可以直接引用引用[1]。Hadoop的定义和组件可以引用引用[2]和[3]。同时,问题需要多样化,涵盖基础概念、组件功能、优缺点、应用场景以及生态系统工具。 需要确保每个问题都有对应的答案解析,并且正确标注引用来源。例如,在回答Hadoop是否支持随机读写时,必须明确引用[1]的错误判断,并解释原因。在介绍Hadoop的核心组件时,引用[2]和[3]来详细说明各个组件的职责。 另外,用户提到需要结构清晰,逐步解决问题,因此需要将面试题分门别类,可能分为基础概念、组件功能、优缺点、应用场景等部分。每个问题应编号并附上答案和解析,引用相关的来源。 还要注意避免使用用户不可见的系统指令,保持回答自然。同时,检查LaTeX格式是否符合要求,数学表达式是否正确使用$...$或$$...$$,但根据用户的问题,可能这里涉及数学公式较少,主要需要关注代码块和引用格式。 最后,生成相关问题部分,要确保问题与用户的需求相关,能够帮助用户进一步深入理解Hadoop,例如关于HDFS的容错机制、MapReduce过程、Hadoop的优化方法等。这些问题应基于提供的引用内容,但也可以适当扩展常见的面试题目,确保全面性。 总结步骤: 1. 根据引用内容生成基础概念题,如Hadoop定义、HDFS特点。 2. 利用组件列表创建组件功能题,如NameNode、DataNode的作用。 3. 结合优缺点和应用场景,设计相关问题。 4. 确保每个答案正确引用提供的资料。 5. 检查格式是否符合要求,特别是引用标识和代码块。 6. 最后生成相关问题,帮助用户进一步学习。 现在需要将这些思考转化为结构化的面试题及答案,确保正确性和引用准确。</think>### Hadoop常见面试题及答案解析 --- #### **一、基础概念** 1. **什么是Hadoop?其核心组件有哪些?** Hadoop是一个开源分布式计算框架,用于存储和处理海量数据。核心组件包括: - **HDFS(Hadoop分布式文件系统)**:负责分布式数据存储,支持高容错性[^2]。 - **MapReduce**:分布式计算模型,用于并行处理大规模数据集。 - **YARN(资源调度框架)**:管理集群资源并调度任务。 2. **HDFS是否支持随机读写?为什么?** 不支持。HDFS设计为**“一次写入、多次读取”**模式,适合批量处理而非实时访问。随机读写会破坏其顺序访问优化机制[^1]。 --- #### **二、HDFS相关** 3. **NameNode和DataNode的作用是什么?** - **NameNode**:管理文件系统元数据(如文件目录结构、块位置),不存储实际数据[^3]。 - **DataNode**:存储实际数据块,并向NameNode汇报状态。 4. **HDFS如何实现容错?** - 数据分块存储(默认128MB/块),每个块复制到多个节点(默认3副本)。 - 若某个DataNode故障,系统会从其他副本恢复数据。 --- #### **三、MapReduce相关** 5. **MapReduce的工作流程分为哪几个阶段?** - **Map阶段**:输入数据分片,由Mapper生成键值对。 - **Shuffle阶段**:按Key排序并分发到Reducer。 - **Reduce阶段**:聚合键值对,输出最终结果。 6. **Combiner的作用是什么?** Combiner是本地Reduce操作,用于合并Mapper输出的中间结果,减少网络传输量。例如,求和时可以先在Mapper端局部汇总。 --- #### **四、优缺点与应用场景** 7. **Hadoop的优缺点有哪些?** - **优点**:高扩展性、容错性、低成本硬件支持。 - **缺点**:不适合低延迟任务(如实时查询)、小文件处理效率低。 8. **Hadoop适合哪些应用场景?** - 日志分析、离线数据处理(如ETL)、大规模数据存储(如历史档案)[^2]。 --- #### **五、进阶问题** 9. **Secondary NameNode的作用是什么?** 定期合并NameNode的编辑日志(EditLog)和镜像文件(FsImage),防止EditLog过大。注意:**它不是NameNode的备份**,仅辅助恢复[^3]。 10. **如何优化Hadoop小文件问题?** - 合并小文件为SequenceFile或HAR文件。 - 使用HBase替代HDFS存储小文件。 --- ### §§扩展问题§§ 1. HDFS的副本放置策略是什么? 2. YARN中ResourceManager和NodeManager的职责有何区别? 3. Hadoop 2.x与Hadoop 3.x的主要区别有哪些? 4. 如何理解MapReduce的“数据本地化”优化? 5. Hadoop生态系统中Hive和HBase的适用场景有何不同? --- **引用来源** [^1]: Hadoop仅支持顺序读写,不支持随机访问。 : Hadoop核心架构与HDFS设计原理。 : Hadoop组件(NameNode、DataNode等)的功能说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值