欢迎光临
我们一直在努力

华为OD机试2025A卷-滑动窗口最大和[100分](Java_Python3_C++_JsNode_Go)实现100%通过率

在这里插入图片描述


前言

华为OD机试2025A卷 是华为外包岗位招聘中机试环节的题目集合,其中"滑动窗口最大和"是一道经典的定长滑动窗口题目。本题考察的是窗口和的递推维护,即窗口每滑动一格时如何 O(1) 更新窗口和。题目分值100分,难度较低,是滑动窗口最基础的应用题。本文将提供5种语言的完整实现(C++、Java、Python3、JavaScript、Go),每种语言的代码都附带详细的中文注释,帮助读者彻底理解定长窗口滑动与窗口和递推的建模与求解过程。建议在阅读代码时,重点关注窗口未满时的扩张、窗口满后整体右移时"减左端、加右端"的递推逻辑。


一:题目描述

滑动窗口最大和

题目名称:滑动窗口最大和

【题目描述】

有一个 N 个整数的数组,和一个长度为 M 的窗口,窗口从数组内的第一个数开始滑动直到窗口不能滑动为止,每次窗口滑动产生一个窗口和(窗口内所有数的和),求窗口滑动产生的所有窗口和的最大值。

【输入格式】

  • 第一行输入一个正整数 N,表示整数个数。(0 < N < 100000)
  • 第二行输入 N 个整数,整数的取值范围为 [-100, 100]。
  • 第三行输入一个正整数 M,M 代表窗口的大小,M <= 100000,且 M <= N。

【输出格式】

窗口滑动产生的所有窗口和的最大值。

【示例1】

输入:

6
10 20 30 15 23 12
3

输出:

68

解释:窗口大小为 3,依次得到窗口和 10+20+30=60、20+30+15=65、30+15+23=68、15+23+12=50,最大值为 68。

【示例2】

输入:

5
1 2 3 4 5
2

输出:

9

解释:窗口大小为 2,窗口和依次为 3、5、7、9,最大值为 9。


二:解题思路

解法一:每个窗口重新求和(不推荐)

暴力做法是对每个窗口位置都把窗口内的 M 个数重新累加一遍。共有 O(N) 个窗口,每个窗口求和 O(M),总时间复杂度 O(N × M)。当 N、M 都达到 10 万时,计算量高达百亿次,完全无法在规定时间内通过,因此必须递推维护窗口和。

解法二:定长窗口滑动 + 窗口和递推(最优解)

核心建模

窗口大小固定为 M。用 left、right 两个指针表示窗口边界,用 tmp 记录当前窗口内数字之和。窗口每向右滑动一格,其实只是移出最左边一个数、加入最右边一个新数,因此窗口和可以 O(1) 递推:

  • 窗口和 = 旧窗口和 − 移出的左端元素 + 新加入的右端元素。

这样全程只需遍历数组一次。

窗口的扩张与滑动

  • 先让右边界一直右移,直到窗口大小达到 M(即 right – left + 1 == M),这段时间只加不减;
  • 窗口大小满 M 后,每滑一格:先减去左端 ans[left]、left++,再加上右端 ans[right]、right++;
  • 每次窗口大小恰好为 M 时,用当前窗口和 tmp 更新最大值 res。
  • 初始最大值要足够小

    数组元素可能为负数,窗口和也可能为负,因此记录最大值的 res 要初始化为一个很小的数(如负无穷),不能初始化为 0,否则全负数据会错误地输出 0。

    实际步骤总结:

  • 读入 N、数组、窗口大小 M。
  • 初始化 left = 0、tmp = 0、res 为一个极小值,右指针 right 从 0 开始。
  • 当窗口大小不足 M 时,只把右端元素加入 tmp,right++。
  • 窗口大小满 M 后,每步先减去左端 ans[left]、left++,再加入右端 ans[right]。
  • 窗口大小为 M 时,用 tmp 更新 res,然后 right++。
  • 遍历结束后输出 res。

  • 三:代码实现

    C++

    #include<iostream>
    #include<vector>
    #include<string>
    #include <utility>
    #include <sstream>
    #include<algorithm>
    #include<list>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;

    int main() {
    int n, m;
    cin >> n;
    vector<int> ans(n);
    for (int i = 0; i < n; i++) {
    cin >> ans[i];
    }
    cin >> m;
    // left 左边界 right右边界下一个选择的位置
    int left = 0, right = 0;
    int res = –100000000;
    int tmp = 0;

    while (right < n) {
    // 只考虑窗口为M的情况
    if (right – left + 1 < m) {
    tmp += ans[right];
    right++;
    continue;
    }
    // 窗口大小为M+1,left 右移
    if (right – left + 1 > m) {
    tmp -= ans[left];
    left++;
    }
    tmp += ans[right];
    res = max(res, tmp);
    right++;
    }

    cout << res;
    return 0;
    }

    Java

    import java.util.*;

    public class Main {
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int n = sc.nextInt(); // 输入数组长度
    int[] ans = new int[n];
    for (int i = 0; i < n; i++) {
    ans[i] = sc.nextInt();
    }

    int m = sc.nextInt(); // 滑动窗口大小

    int left = 0, right = 0;
    int res = Integer.MIN_VALUE;
    int tmp = 0;

    while (right < n) {
    // 窗口还不够大,继续加元素
    if (right – left + 1 < m) {
    tmp += ans[right];
    right++;
    continue;
    }

    // 窗口太大了,缩小窗口
    if (right – left + 1 > m) {
    tmp -= ans[left];
    left++;
    }

    tmp += ans[right];
    res = Math.max(res, tmp);
    right++;
    }

    System.out.println(res);
    }
    }

    Python3

    # -*- coding: utf-8 -*-
    n = int(input())
    ans = list(map(int, input().split()))
    m = int(input())

    left = 0
    right = 0
    res = –10**9
    tmp = 0

    while right < n:
    # 窗口还没满,继续加
    if right – left + 1 < m:
    tmp += ans[right]
    right += 1
    continue

    # 窗口太大了,缩小
    if right – left + 1 > m:
    tmp -= ans[left]
    left += 1

    tmp += ans[right]
    res = max(res, tmp)
    right += 1

    print(res)

    JavaScript

    const readline = require("readline");

    const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
    });

    let inputLines = [];

    rl.on("line", function (line) {
    inputLines.push(line.trim());
    if (inputLines.length === 3) {
    main();
    rl.close();
    }
    });

    function main() {
    const n = parseInt(inputLines[0]);
    const ans = inputLines[1].split(' ').map(Number);
    const m = parseInt(inputLines[2]);

    let left = 0, right = 0;
    let res = –1e9;
    let tmp = 0;

    while (right < n) {
    // 窗口不够大
    if (right – left + 1 < m) {
    tmp += ans[right];
    right++;
    continue;
    }

    // 窗口太大
    if (right – left + 1 > m) {
    tmp -= ans[left];
    left++;
    }

    tmp += ans[right];
    res = Math.max(res, tmp);
    right++;
    }

    console.log(res);
    }

    Go

    package main

    import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
    )

    func main() {
    reader := bufio.NewReader(os.Stdin)

    // 读取 n
    line, _ := reader.ReadString('\\n')
    n, _ := strconv.Atoi(strings.TrimSpace(line))

    // 读取数组
    line, _ = reader.ReadString('\\n')
    fields := strings.Fields(line)
    ans := make([]int, n)
    for i := 0; i < n; i++ {
    ans[i], _ = strconv.Atoi(fields[i])
    }

    // 读取 m
    line, _ = reader.ReadString('\\n')
    m, _ := strconv.Atoi(strings.TrimSpace(line))

    left, right := 0, 0
    res := –100000000
    tmp := 0

    for right < n {
    if right–left+1 < m {
    tmp += ans[right]
    right++
    continue
    }

    if right–left+1 > m {
    tmp -= ans[left]
    left++
    }

    tmp += ans[right]
    if tmp > res {
    res = tmp
    }
    right++
    }

    fmt.Println(res)
    }


    四:复杂度分析

    指标分析
    时间复杂度 O(N)。左右指针都只单向遍历数组一次,窗口和通过"减左端、加右端"O(1) 递推,无需每个窗口重新求和。
    空间复杂度 O(N),需要存储整个输入数组。
    适用数据范围 N、M 可达 10 万,线性算法可在毫秒级完成;若用 O(N×M) 暴力求和则会超时。

    五:易错点

  • 最大值初值要够小:数组元素范围含负数,res 必须初始化为负无穷(如 Integer.MIN_VALUE、-1e9),不能初始化为 0,否则全负数据会错答为 0。

  • 窗口和递推别重复加:窗口满后每步先减左端、再加右端,注意顺序,不要把新右端重复累加导致窗口和偏大。

  • 窗口未满只加不减:右边界扩张到 M 之前只加元素、不收缩左边界,否则窗口大小会一直对不上。

  • 数据范围:窗口和虽不大,但 N 达 10 万,读取输入要高效;本题元素在 [-100,100],窗口和不会溢出 int。

  • M 与 N 的关系:题目保证 M <= N,窗口至少能形成一个;注意窗口个数为 N – M + 1,别漏统计最后一个窗口。


  • GIF

    赞(0)
    未经允许不得转载:171主机测评 » 华为OD机试2025A卷-滑动窗口最大和[100分](Java_Python3_C++_JsNode_Go)实现100%通过率
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址