LeetCode 2090. K Radius Subarray Averages【前缀和,滑动窗口,数组】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给你一个下标从 0 开始的数组 nums ,数组中有 n 个整数,另给你一个整数 k 。

半径为 k 的子数组平均值 是指:nums 中一个以下标 i 为 中心 且 半径 为 k 的子数组中所有元素的平均值,即下标在 i - k 和 i + k 范围( i - k 和 i + k)内所有元素的平均值。如果在下标 i 前或后不足 k 个元素,那么 半径为 k 的子数组平均值 是 -1 。

构建并返回一个长度为 n 的数组 avgs ,其中 avgs[i] 是以下标 i 为中心的子数组的 半径为 k 的子数组平均值 。

x 个元素的 平均值 是 x 个元素相加之和除以 x ,此时使用截断式 整数除法 ,即需要去掉结果的小数部分。

  • 例如,四个元素 231 和 5 的平均值是 (2 + 3 + 1 + 5) / 4 = 11 / 4 = 2.75,截断后得到 2 。

示例 1:
LeetCode 2090. K Radius Subarray Averages【前缀和,滑动窗口,数组】中等_第1张图片

输入:nums = [7,4,3,9,1,8,5,2,6], k = 3
输出:[-1,-1,-1,5,4,4,-1,-1,-1]
解释:
- avg[0]、avg[1] 和 avg[2]-1 ,因为在这几个下标前的元素数量都不足 k 个。
- 中心为下标 3 且半径为 3 的子数组的元素总和是:7 + 4 + 3 + 9 + 1 + 8 + 5 = 37 。
  使用截断式 整数除法,avg[3] = 37 / 7 = 5- 中心为下标 4 的子数组,avg[4] = (4 + 3 + 9 + 1 + 8 + 5 + 2) / 7 = 4- 中心为下标 5 的子数组,avg[5] = (3 + 9 + 1 + 8 + 5 + 2 + 6) / 7 = 4- avg[6]、avg[7] 和 avg[8]-1 ,因为在这几个下标后的元素数量都不足 k 个。

示例 2:

输入:nums = [100000], k = 0
输出:[100000]
解释:
- 中心为下标 0 且半径 0 的子数组的元素总和是:100000 。
  avg[0] = 100000 / 1 = 100000

示例 3:

输入:nums = [8], k = 100000
输出:[-1]
解释:
- avg[0]-1 ,因为在下标 0 前后的元素数量均不足 k 。

提示:

  • n == nums.length
  • 1 <= n <= 10^5
  • 0 <= nums[i], k <= 10^5

解法1 前缀和

根据题目描述,只有当中心位置 i ∈ [ k , n − k − 1 ] i \in [k, n-k-1] i[k,nk1] 时,整个长度为 2 k + 1 2k+1 2k+1 的子区间才会完整地落在数组 n u m s nums nums 内部。当 i < k ii<k 或者 i ≥ n − k i≥n−k ink 时,对应的平均值为 − 1 -1 1

因此如果 k ≥ n − k − 1 k \geq n-k-1 knk1 2 k + 1 ≥ n 2k+1≥n 2k+1n ,答案数组中所有的元素均为 − 1 -1 1 。否则首先计算出数组 n u m s nums nums 的前缀和 s u m sum sum ,然后对 i ∈ [ k ,   n − k − 1 ] i \in [k,\ n - k - 1] i[k, nk1] 中的所有位置,利用前缀和求其 [ i − k , i + 1 ] [i -k , i +1] [ik,i+1] 的元素和、并除以 2 k + 1 2k+1 2k+1 s u m [ i + k + 1 ] − s u m [ i − k ] 2 k − 1 \dfrac{sum[i + k + 1] - sum[i - k]}{2k-1} 2k1sum[i+k+1]sum[ik]
注意,前缀和数组要用 long long ,不然会溢出。

class Solution {
    public int[] getAverages(int[] nums, int k) {
        if (k == 0) return nums;
        int n = nums.length, m = 2 * k + 1;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        if (m > n) return ans;
        long[] sum = new long[n + 1];
        for (int i = 0; i < n; ++i) sum[i + 1] = sum[i] + nums[i];
        for (int i = k; i + k < n; ++i) ans[i] = (int)((sum[i + k + 1] - sum[i - k]) / m);
        return ans;
    }
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

解法2 滑动窗口

不难发现,上述做法需要 O ( n ) O(n) O(n) 的空间来存储前缀和,但实际上可以进一步优化,且只需遍历一次。

首先,先求出前 2 k + 1 2k+1 2k+1 个元素的和,放在答案数组的 a n s [ k ] ans[k] ans[k] 中。由于:
{ ans [ i − 1 ] = nums [ i − k − 1 ] + nums [ i − k ] + ⋯ + nums [ i + k − 1 ] ans [ i ] = nums [ i − k ] + ⋯ + nums [ i + k − 1 ] + nums [ i + k ] \left\{ \begin{aligned} & \textit{ans}[i - 1] && = \textit{nums}[i - k - 1] + \textit{nums}[i - k] + \cdots + \textit{nums}[i + k - 1] \\ & \textit{ans}[i] && = \textit{nums}[i - k] + \cdots + \textit{nums}[i + k - 1] + \textit{nums}[i + k] \end{aligned} \right. {ans[i1]ans[i]=nums[ik1]+nums[ik]++nums[i+k1]=nums[ik]++nums[i+k1]+nums[i+k]
​因此随后只需要通过递推式:
ans [ i ] = ans [ i − 1 ] + nums [ i + k ] − nums [ i − k − 1 ] \textit{ans}[i] = \textit{ans}[i - 1] + \textit{nums}[i + k] - \textit{nums}[i - k - 1] ans[i]=ans[i1]+nums[i+k]nums[ik1]

即可得到所有中心位置 i ∈ [ k , n − k − 1 ] i \in [k, n-k-1] i[k,nk1] 且长度为 2 k + 1 2k+1 2k+1 的子数组的和。最后将每一个和除以 2 k + 1 2k+1 2k+1 即可得到平均数。

class Solution {
    public int[] getAverages(int[] nums, int k) {
        if (k == 0) return nums;
        int n = nums.length, m = 2 * k + 1;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        if (m > n) return ans;
        long sum = 0;
        for (int i = 0; i < m; ++i) sum += nums[i];
        for (int i = k; i + k < n; ++i) {
            if (i != k) sum += nums[i + k] - nums[i - k - 1];
            ans[i] = (int)(sum / m);
        }
        return ans;
    }
}

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1) ,不计算答案数组的情况下,只使用了若干辅助变量

你可能感兴趣的:(#,滑动窗口,算法技巧-前缀和,数组,leetcode,算法,职场和发展)