一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
396-旋转函数
题目描述
给定一个长度为
n的整数数组nums。假设
arrk是数组nums顺时针旋转k个位置后的数组,我们定义nums的 旋转函数F为:
F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1]返回F(0), F(1), ..., F(n-1)中的最大值 。生成的测试用例让答案符合 32 位 整数。
示例
示例 1:
输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26
所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。
示例 2:
输入: nums = [100]
输出: 0
提示
n == nums.length1 <= n <= 105-100 <= nums[i] <= 100
题解
思路
在示例中
原始数组:[4,3,2,6]:F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 25
旋转 1 次:[6,4,3,2]:F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 16
旋转 2 次:[2,6,4,3]:F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 23
旋转 3 次:[3,2,6,4]:F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 26
从 F0 到 F1 的区别在于:0 * 4 变成了 1 * 4,1 * 3 变成了 2 * 3,2 * 2 变成了 3 * 2,3 * 6 变成了 0 * 6
从 F1 到 F2 的区别在于:0 * 6 变成了 1 * 6,1 * 4 变成了 2 * 4,2 * 3 变成了 3 * 3,3 * 2 变成了 0 * 2
从 F2 到 F3 的区别在于:0 * 2 变成了 1 * 2,1 * 6 变成了 2 * 6,2 * 4 变成了 3 * 4,3 * 3 变成了 0 * 3
这样的话就很容易看出规律了,在数组 [4,3,2,6] ,当旋转一次后变成了 [6,4,3,2],其中的 3,2,1 在原有的基础上各加一次,而 6 从 3 * 6 到 0 * 6 相当于减了 3 次,而下面的每旋转一次数组,总和都是在上一个数的基础上变化的,变化规则就是其中 nums[1] 到 nums[n - 1] 各加一次,nums[0] 减去三次,这样虽然有规律,但是代码不好写,我们可以让 nums[0] 到 nums[n - 1] 各加一次,然后 nums[0] 减去四次,而且减去的四次刚好就是数组的长度。
假设数组的总和为 sum,那么每旋转一次有以下规律:
F(i) = F(i - 1) + sum - nums[0] * nums.length
代码
public int maxRotateFunction(int[] nums) {
int n = nums.length;
int sum = 0;
int f = 0;
for (int i = 0; i < n; i++) {
sum += nums[i];
f += nums[i] * i;
}
int res = f;
for (int i = n - 1; i > 0; i--) {
f = f + sum - nums[i] * n;
res = Math.max(res, f);
}
return res;
}
代码解析
首先第一轮循环将原始数组的总和 f 计算出来,并且计算出所有元素的总和 sum。
然后由于是顺时针旋转,所以从数组的最后一个元素开始遍历,每次遍历的 f 为 f + sum - nums[i] * n,然后计算出最大的总和保存到 res中返回即可。