Hard
题目描述
给定一个数组 nums,你可以将其向右旋转 k 个位置,使数组变为 [nums[k], nums[k + 1], ... nums[nums.length - 1], nums[0], nums[1], ..., nums[k-1]]。旋转后,任何小于等于其下标的元素都可以得到一分。
例如,如果我们有 nums = [2,4,1,3,0],向右旋转 k = 2 位置后变为 [1,3,0,2,4]。这样可以得到 3 分,因为:
1 > 0(不得分)3 > 1(不得分)0 <= 2(得 1 分)2 <= 3(得 1 分)4 <= 4(得 1 分)
返回能够得到最高分数的旋转下标 k。如果有多个答案,返回最小的 k。
示例 1:
输入:nums = [2,3,1,4,0]
输出:3
解释:每个 k 的得分如下:
k = 0, nums = [2,3,1,4,0], 得分 2
k = 1, nums = [3,1,4,0,2], 得分 3
k = 2, nums = [1,4,0,2,3], 得分 3
k = 3, nums = [4,0,2,3,1], 得分 4
k = 4, nums = [0,2,3,1,4], 得分 3
所以我们应该选择 k = 3,它有最高的得分。
示例 2:
输入:nums = [1,3,0,2,4]
输出:0
解释:无论如何旋转,nums 总是得到 3 分。
所以我们选择最小的 k,即 0。
提示:
1 <= nums.length <= 10^50 <= nums[i] < nums.length
解题思路
解题思路
这是一道需要巧妙运用差分数组的题目。暴力解法时间复杂度为 O(n²),会超时,需要优化。
核心观察: 对于每个元素 nums[i],我们需要找出在哪些旋转位置 k 下,该元素能够得分(即 nums[i] <= 新位置)。
分析元素得分条件:
- 原位置为
i的元素,旋转k位置后的新位置为(i - k + n) % n - 得分条件:
nums[i] <= (i - k + n) % n
关键洞察: 每个元素在某个 k 值区间内能得分,在区间外不得分。我们需要找到每个元素的"失分区间",然后用差分数组统计。
具体步骤:
- 对每个元素
nums[i],计算它在什么情况下不能得分 - 不能得分的条件:
nums[i] > (i - k + n) % n - 通过数学推导,元素
nums[i]在区间[(i - nums[i] + 1 + n) % n, i]内旋转时会失分 - 使用差分数组记录每个旋转位置的得分变化
- 找到得分最高且
k最小的位置
差分数组优化: 通过差分数组,我们可以在 O(1) 时间内更新区间,总时间复杂度降为 O(n)。
代码实现
class Solution {
public:
int bestRotation(vector<int>& nums) {
int n = nums.size();
vector<int> change(n, 0);
for (int i = 0; i < n; i++) {
// 计算元素nums[i]在位置i时不能得分的旋转区间
int left = (i - nums[i] + 1 + n) % n;
int right = (i + 1) % n;
// 差分数组标记失分区间
change[left] -= 1;
change[right] += 1;
}
int maxScore = 0, bestK = 0, currentScore = 0;
// 计算每个k位置的得分变化
for (int k = 0; k < n; k++) {
currentScore += change[k];
if (currentScore > maxScore) {
maxScore = currentScore;
bestK = k;
}
}
return bestK;
}
};
class Solution:
def bestRotation(self, nums: List[int]) -> int:
n = len(nums)
change = [0] * n
for i in range(n):
# 计算元素nums[i]在位置i时不能得分的旋转区间
left = (i - nums[i] + 1 + n) % n
right = (i + 1) % n
# 差分数组标记失分区间
change[left] -= 1
change[right] += 1
max_score = 0
best_k = 0
current_score = 0
# 计算每个k位置的得分变化
for k in range(n):
current_score += change[k]
if current_score > max_score:
max_score = current_score
best_k = k
return best_k
public class Solution {
public int BestRotation(int[] nums) {
int n = nums.Length;
int[] change = new int[n];
for (int i = 0; i < n; i++) {
// 计算元素nums[i]在位置i时不能得分的旋转区间
int left = (i - nums[i] + 1 + n) % n;
int right = (i + 1) % n;
// 差分数组标记失分区间
change[left] -= 1;
change[right] += 1;
}
int maxScore = 0, bestK = 0, currentScore = 0;
// 计算每个k位置的得分变化
for (int k = 0; k < n; k++) {
currentScore += change[k];
if (currentScore > maxScore) {
maxScore = currentScore;
bestK = k;
}
}
return bestK;
}
}
var bestRotation = function(nums) {
const n = nums.length;
const change = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
// 计算元素nums[i]在位置i时不能得分的旋转区间
const left = (i - nums[i] + 1 + n) % n;
const right = (i + 1) % n;
// 差分数组标记失分区间
change[left] -= 1;
change[right] += 1;
}
let maxScore = 0, bestK = 0, currentScore = 0;
// 计算每个k位置的得分变化
for (let k = 0; k < n; k++) {
currentScore += change[k];
if (currentScore > maxScore) {
maxScore = currentScore;
bestK = k;
}
}
return bestK;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 遍历数组两次,每次O(n) |
| 空间复杂度 | O(n) | 需要额外的差分数组存储得分变化 |