
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。
一、题目描述
小菊和小花是好朋友,他们经常一起玩游戏。这天他们玩一个数字游戏,获胜可以获得对方1杯奶茶。
游戏规则:
小菊在纸上写了一排数组,小花需要从中选择连续k个数字,使得这k个数字的和最大。
小花正确找到最大的值就是获胜,小菊则提供1杯奶茶;小花想获胜喝奶茶,请你帮助她。
二、输入描述
-
参数 1:给出的一排数字
-
参数2:k
约束:
-
-2147483648 <= 单个数字 <= 2147483647
-
0 < 一排数字长度 <1063
-
1 <=k <= 一排数字的长度
三、输出描述
最大k个数字和
四、测试用例
测试用例1:
1、输入
1,3,8,6,4,10
3
2、输出
20
3、说明
连续三个数字的窗口分别为:
1+3+8=12
3+8+6=17
8+6+4=18
6+4+10=20
所以最大值为 20。
测试用例2:
1、输入
-5,-2,-3,-4
2
2、输出
-5
3、说明
三个窗口分别为 -7、-5、-7,最大值为 -5。此用例用于验证不能把最大值错误初始化为 0。
五、解题思路
本题要求从一排数字中找出连续 k 个数字,使这 k 个数字之和最大。因为要求选择的数字必须连续,而且窗口长度 k 固定,所以适合使用滑动窗口算法。
首先计算数组前 k 个元素的总和,将其作为第一个长度为 k 的窗口,并记录为当前最大值 maxSum。随后让窗口从左向右移动。当窗口向右移动一位时,原窗口最左边的元素会离开,同时右侧会有一个新的元素进入。因此无需重新遍历 k 个元素求和,只需要用当前窗口和减去离开的元素,再加上进入的元素,即可在 O(1) 时间得到新窗口的总和,然后与 maxSum 比较并更新最大值。
如果采用暴力方法,对每一个起点都重新计算连续 k 个数字的和,最坏情况下时间复杂度为 O(n × k)。使用滑动窗口后,只需要完整遍历数组一次,因此时间复杂度降低为 O(n)。
数据存储使用 long 数组。虽然题目规定单个数字的范围是 32 位 int,但连续 k 个数字的总和可能超过 int 的最大值 2147483647。例如 2147483647 + 2147483647 = 4294967294,因此窗口和必须使用 long,避免整数溢出。
算法时间复杂度为 O(n),额外空间复杂度为 O(n),其中 n 为数字个数。核心算法只维护 windowSum 和 maxSum 两个变量,逻辑简单,同时能够正确处理正数、负数、k=1、k等于数组长度等边界情况。
六、Python算法源码
# 第一行读取逗号分隔的数字
line = input().strip()
# 第二行读取窗口长度 k
k = int(input().strip())
nums = [int(x.strip()) for x in line.split(",")]
# 先计算第一个长度为 k 的窗口
window_sum = sum(nums[:k])
max_sum = window_sum
# 滑动窗口:
# 减去离开窗口的元素,同时加入新进入窗口的元素
for right in range(k, len(nums)):
window_sum += nums[right] - nums[right - k]
if window_sum > max_sum:
max_sum = window_sum
print(max_sum)
七、JavaScript算法源码
const fs = require("fs");
const input = fs.readFileSync(0, "utf8")
.trim()
.split(/\r?\n/);
// 使用 BigInt。
// 虽然单个数字位于 32 位整数范围,
// 但是多个数字的和可能超过 JavaScript Number 的安全整数范围。
const nums = input[0]
.split(",")
.map(item => BigInt(item.trim()));
const k = Number(input[1].trim());
// 计算第一个长度为 k 的窗口
let windowSum = 0n;
for (let i = 0; i < k; i++) {
windowSum += nums[i];
}
let maxSum = windowSum;
/*
* 窗口每向右移动一位:
* 减去离开窗口的数字,
* 加上新进入窗口的数字。
*/
for (let right = k; right < nums.length; right++) {
windowSum += nums[right] - nums[right - k];
if (windowSum > maxSum) {
maxSum = windowSum;
}
}
// BigInt 转换为字符串输出,避免输出末尾的 n
console.log(maxSum.toString());
八、C算法源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
/*
* 根据题目给出的数据规模,
* 使用字符数组读取第一行逗号分隔的数字。
*/
char line[65536];
if (fgets(line, sizeof(line), stdin) == NULL) {
return 0;
}
int k;
if (scanf("%d", &k) != 1) {
return 0;
}
/*
* 单个数字虽然是 int 范围,
* 但多个数字之和可能超过 int,
* 因此使用 long long。
*/
long long nums[2000];
int n = 0;
// 使用 strtok 按逗号拆分第一行
char *token = strtok(line, ",\r\n");
while (token != NULL) {
nums[n++] = strtoll(token, NULL, 10);
token = strtok(NULL, ",\r\n");
}
// 计算第一个长度为 k 的窗口
long long windowSum = 0;
for (int i = 0; i < k; i++) {
windowSum += nums[i];
}
long long maxSum = windowSum;
/*
* 使用滑动窗口遍历剩余元素。
* 每次减去窗口最左侧元素,
* 再加入新的右侧元素。
*/
for (int right = k; right < n; right++) {
windowSum += nums[right] - nums[right - k];
if (windowSum > maxSum) {
maxSum = windowSum;
}
}
printf("%lld\n", maxSum);
return 0;
}
九、C++算法源码
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
string line;
getline(cin, line);
int k;
cin >> k;
/*
* 使用 vector<long long> 保存数字。
* 使用 long long 是因为连续 k 个 int 相加后,
* 结果可能超过 32 位 int 范围。
*/
vector<long long> nums;
string token;
stringstream ss(line);
// 根据逗号解析第一行
while (getline(ss, token, ',')) {
nums.push_back(stoll(token));
}
// 计算第一个长度为 k 的窗口
long long windowSum = 0;
for (int i = 0; i < k; i++) {
windowSum += nums[i];
}
long long maxSum = windowSum;
/*
* 滑动窗口向右移动。
* 当前窗口和 = 原窗口和
* - 离开窗口的元素
* + 新进入窗口的元素。
*/
for (int right = k; right < static_cast<int>(nums.size()); right++) {
windowSum += nums[right] - nums[right - k];
maxSum = max(maxSum, windowSum);
}
cout << maxSum << '\n';
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 新系统 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

273

被折叠的 条评论
为什么被折叠?



