1. 题目背景与核心需求解析
这道来自华为OD机试的真题考察的是经典的滑动窗口算法应用。题目要求我们从一个整数数组中,找出所有连续子数组(窗口)中元素和最大的那个值。这类问题在实际工程中非常常见,比如网络流量监控、股票交易分析等场景都需要类似的计算。
举个生活中的例子:假设你连续30天记录奶茶店每日销售额,现在需要找出任意连续7天内总销售额最高的时段。这就是典型的滑动窗口求最大和问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法思路与复杂度分析
2.1 暴力解法及其缺陷
最直观的解法是双重循环遍历所有可能的子数组:
cpp复制int maxSum = INT_MIN;
for(int i=0; i<n; i++){
int currentSum = 0;
for(int j=i; j<i+k && j<n; j++){
currentSum += nums[j];
}
maxSum = max(maxSum, currentSum);
}
这种方法时间复杂度为O(n*k),当n较大时效率很低,无法通过华为OD的测试用例。
2.2 滑动窗口优化方案
更高效的解法是维护一个窗口,通过增减窗口两端元素来更新和值:
cpp复制int windowSum = 0;
for(int i=0; i<k; i++){
windowSum += nums[i];
}
int maxSum = windowSum;
for(int i=k; i<n; i++){
windowSum += nums[i] - nums[i-k];
maxSum = max(maxSum, windowSum);
}
这样时间复杂度降为O(n),空间复杂度O(1)。
关键技巧:窗口滑动时只需减去离开窗口的元素并加上新进入窗口的元素,避免重复计算。
3. C++实现细节与边界处理
3.1 完整代码实现
cpp复制#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int maxSlidingWindowSum(vector<int>& nums, int k) {
if(nums.empty() || k <=0 || k > nums.size())
return 0;
int windowSum = 0;
for(int i=0; i<k; i++){
windowSum += nums[i];
}
int maxSum = windowSum;
for(int i=k; i<nums.size(); i++){
windowSum += nums[i] - nums[i-k];
maxSum = max(maxSum, windowSum);
}
return maxSum;
}
int main() {
vector<int> nums = {1,3,-1,-3,5,3,6,7};
int k = 3;
cout << "最大滑动窗口和为: " << maxSlidingWindowSum(nums, k) << endl;
return 0;
}
3.2 边界条件处理要点
- 空数组检查:
if(nums.empty()) - 窗口大小合法性:
k <=0 || k > nums.size() - 整数溢出:使用
long类型处理大数情况 - 全负数数组:初始值设为
INT_MIN
4. 华为OD机试实战技巧
4.1 输入输出处理规范
华为OD机试通常需要处理标准输入输出:
cpp复制int main() {
int n, k;
cin >> n >> k;
vector<int> nums(n);
for(int i=0; i<n; i++){
cin >> nums[i];
}
cout << maxSlidingWindowSum(nums, k);
return 0;
}
4.2 性能优化建议
- 使用
ios::sync_with_stdio(false)加速IO - 预分配vector空间避免动态扩容
- 使用局部变量而非全局变量
- 避免不必要的函数调用
5. 算法扩展与变种
5.1 滑动窗口最小值
类似思路可以解决LeetCode 239题:
cpp复制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[dq.back()] < nums[i])
dq.pop_back();
dq.push_back(i);
if(i >= k-1) res.push_back(nums[dq.front()]);
}
return res;
}
5.2 动态窗口大小问题
当窗口大小不固定时(如求和≥target的最短子数组),可以使用滑动窗口的变种:
cpp复制int minSubArrayLen(int target, vector<int>& nums) {
int left = 0, sum = 0;
int minLen = INT_MAX;
for(int right=0; right<nums.size(); right++){
sum += nums[right];
while(sum >= target){
minLen = min(minLen, right-left+1);
sum -= nums[left++];
}
}
return minLen == INT_MAX ? 0 : minLen;
}
6. 常见错误与调试技巧
6.1 典型错误案例
- 窗口索引越界:
cpp复制// 错误写法
for(int i=0; i<=n-k; i++){ // 当k=0时出错
// ...
}
- 初始值设置不当:
cpp复制int maxSum = 0; // 当所有数为负数时出错
6.2 调试建议
- 打印窗口位置和当前和值:
cpp复制cout << "窗口[" << i-k+1 << "," << i << "] 和=" << windowSum << endl;
- 使用边界测试用例:
- 空数组
- k=0
- k=n
- 全负数数组
- 大数测试
7. 华为OD备考建议
- 重点掌握以下数据结构:
- 数组/字符串处理
- 哈希表应用
- 栈/队列/堆
- 链表操作
- 二叉树遍历
- 高频算法题型:
- 滑动窗口
- 双指针
- 深度优先搜索
- 动态规划
- 贪心算法
- 时间分配建议:
- 5分钟理解题意
- 10分钟设计算法
- 15分钟编码实现
- 5分钟测试调试
在实际编码时,我习惯先写出暴力解法确保理解正确,再逐步优化。对于滑动窗口问题,特别注意窗口的左右边界移动条件,这是最容易出错的地方。华为OD的测试用例往往包含一些极端情况,务必在提交前自行测试各种边界条件。
