zl程序教程

您现在的位置是:首页 >  其他

当前栏目

leetcode 643. 子数组最大平均数 I ------滑动窗口篇六,前缀和篇二

2023-03-14 22:54:01 时间

子数组最大平均数 I


暴力法思想

从数组头部开始,依次枚举所有长度为k的连续子数组,对其求和,从中找出最大值

class Solution {
public:
	double findMaxAverage(vector<int>& nums, int k) 
	{
		int l = 0, r = k;
		double res = INT_MIN;
		while (r <=nums.size())
		{
			//注意:左闭右开的区间
			double sum=accumulate(nums.begin()+l, nums.begin() + r, 0);
			res = max(res, sum/k);
			l++, r++;
		}
		return res;
	}
};

暴力法的优化思路

思路:

每次的子数组不能重新计算,会超时, 采用减去上一次的首段再加上新的尾部来计算

代码:

class Solution {
public:
	double findMaxAverage(vector<int>& nums, int k) 
	{
		int l = 0, r = k;
		int sum, res;
		sum =res = accumulate(nums.begin() + l, nums.begin() + r, 0);
		while (r <nums.size())
		{
			sum = sum + nums[r]-nums[l];
			res = max(res, sum);
			l++, r++;
		}
		return (double)res/k;
	}
};

滑动窗口

题目可以抽象成长度固定为 k 的滑动窗口。当每次窗口右移的时候,需要把右边的新位置 加到 窗口中的 和 中,把左边被移除的位置从窗口的 和 中 减掉。这样窗口里面所有元素的 和 是准确的,我们求出最大的和,最终除以 k 得到最大平均数。

这个方法只用遍历一次数组。

需要注意的是,需要根据 i 的位置,计算滑动窗口是否开始、是否要移除最左边元素:

  • 当 i >= k 时,为了固定窗口的元素是 k 个,每次移动时需要将 i - k 位置的元素移除。
  • 当 i >= k - 1 时,最左边第一个滑动窗口内的元素刚好 k 个,开始计算滑动窗口的最大和。

代码:

class Solution {
public:
	double findMaxAverage(vector<int>& nums, int k) 
	{
		int sum=0, res = INT_MIN;
		for (int i = 0; i < nums.size(); i++)
		{
			sum += nums[i];
			if (i >= k)
				sum -= nums[i - k];//当前滑动窗口的长度超了一个,减去最左边的多出的元素
			if (i >= k - 1)
				res = max(res, sum);
		}
		return (double)res/k;
	}
};

前缀和

今天题目让求最大平均数,由于 k 是不变的,因此可以先求区间的最大和,然后再除以 k。

求区间的和可以用 preSum。preSum 方法还能快速计算指定区间段 i ~ j 的元素之和。它的计算方法是从左向右遍历数组,当遍历到数组的 i 位置时,preSum 表示 i 位置左边的元素之和。

假设数组长度为 N,我们定义一个长度为 N+1 的 preSum 数组,preSum[i] 表示该元素左边所有元素之和(不包含当前元素)。然后遍历一次数组,累加区间 [0, i) 范围内的元素,可以得到 preSum 数组。

利用 preSum 数组,可以在 O(1) 的时间内快速求出 nums 任意区间 [i, j] (两端都包含) 的各元素之和。

sum(i, j) = preSum[j + 1] - preSum[i]

对于本题,可以先遍历一次,求数组每个位置的 preSum,然后再遍历一次,求长度为 k 的每个区间的最大和。最终除以 k 得到最大平均数。

代码:

class Solution {
public:
	double findMaxAverage(vector<int>& nums, int k) 
	{
         //在原数组上直接求出前缀和数组,这样可以节省空间消耗
		for (int i = 1; i < nums.size(); i++)
			nums[i] += nums[i - 1];
		int res = nums[k-1];
		for (int i = k; i < nums.size(); i++)
			res = max(res, nums[i] - nums[i - k]);
		return (double)res/k;
	}
};