力扣396. 旋转函数 滑动窗口+前缀和详细解析

214 阅读2分钟

396. 旋转函数

思路分析

首先要求一个区间的值,首先想到滑动窗口,通过一个窗口不停的移动来计算窗口内的值。由于题目说旋转数组,所以我们可以把两个数组进行拼接,然后通过一个大小为n(n为nums.length)的窗口在新拼接的数组上进行移动来求值。 这样做显然时间复杂度会比较高,因为每次移动窗口都要重新计算窗口内的值,如果用滑动窗口来做的逻辑因该是这样

for(新拼接的数组的大小){
	for(窗口的大小){
		求出窗口内的值;
	}
}

只用滑动窗口的话时间复杂度太高,所以要进行优化。观察F(k)公式,当滑动窗口向后移动一位时原来的 F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1] 这个公式就变成了 F(k+1) = 0 * arrk[1] + 1* arrk[2] + ... + (n) * arrk[n] F(k+1)相比F(k)除了新增加了(n) * arrk[n]的值,原先的1 * arrk[1]变成了0 * arrk[1] 原先的2* arrk[2]变成了1* arrk[2]都减少乘数都减少了1。比如下边这个例子 在这里插入图片描述 当窗口向右移动一位时 在这里插入图片描述 原来的(1*3)变成了(0*3),原来的(2*2)变成了(1*2),此后的每一个数字都是如此。 到这里我们可以总结出结论了当滑动窗口向后移动一位的时候就会减少红色区间内的和 在这里插入图片描述 现在就可以得出大致的思路了:将两个数组拼接,使用大小为n的窗口在新的数组上进行滑动,窗口每次向右滑动一格就会加上一个新的值,然后减去原先窗口中1-n这个区间的数字和。 至于如何快速获得区间和,那就是前缀和数组了 实际上我们并不用真正的进行拼接,拼接只是为了思路上更清晰,我们可以使用%来模拟拼接

下面直接看代码

代码实现

class Solution {
    public int maxRotateFunction(int[] nums) {
        int n=nums.length;
        // 创建前缀和数组
        int[] sum=new int[n*2+1];	// 这里要加上1防止越界
        for(int i=1;i<=2*n;i++){
            sum[i]=sum[i-1]+nums[(i-1)%n];
        }
        // 先计算最开始的滑动窗口内的元素的值
        int ans=0;
        for(int i=0;i<n;i++){
            ans+=nums[i]*i;
        }
        // 开始移动窗口
        for(int i=n+1,cur=ans;i<=2*n;i++){
        	// 每次移动窗口都将新的元素加入
            cur+=nums[(i-1)%n]*(n-1);
            // 减去旧的窗口中的元素的和
            cur-=sum[i-1]-sum[i-n];
            // 比较大小保证返回最大值
            if(cur>ans) ans=cur;
        }
        return ans;
    }
}

总结

这题的关键就是要看出在滑动窗口的移动过程中,窗口中的值是如何变化的,并且能根据变化使用前缀和数组快速的获取区间和。