Medium
题目描述
给定一个大小为 n 的整数数组 nums,其中 n 是偶数,以及一个整数 k。
你可以对数组进行一些修改,在一次修改中,你可以将数组中的任意元素替换为范围 [0, k] 内的任意整数。
你需要进行一些修改(可能不修改),使得最终数组满足以下条件:
- 存在一个整数
X,使得对于所有0 <= i < n,都有abs(a[i] - a[n - i - 1]) = X。
返回满足上述条件所需的最少修改次数。
示例 1:
输入:nums = [1,0,1,2,4,3], k = 4
输出:2
解释:
我们可以进行以下修改:
- 将 nums[1] 替换为 2。结果数组为 nums = [1,2,1,2,4,3]。
- 将 nums[3] 替换为 3。结果数组为 nums = [1,2,1,3,4,3]。
整数 X 为 2。
示例 2:
输入:nums = [0,1,2,3,3,6,5,4], k = 6
输出:2
解释:
我们可以进行以下操作:
- 将 nums[3] 替换为 0。结果数组为 nums = [0,1,2,0,3,6,5,4]。
- 将 nums[4] 替换为 4。结果数组为 nums = [0,1,2,0,4,6,5,4]。
整数 X 为 4。
约束条件:
2 <= n == nums.length <= 10^5n是偶数0 <= nums[i] <= k <= 10^5
解题思路
这个问题的核心是要找到一个目标差值 X,使得通过最少的修改次数让所有对称位置的元素差值都等于 X。
解题思路:
配对分析:将数组按对称位置配对,即
(nums[i], nums[n-1-i]),共有 n/2 对。枚举目标差值:X 的可能取值范围是 [0, k]。对于每个可能的 X,计算需要的修改次数。
计算修改次数:对于每一对
(a, b)和目标差值 X:- 如果
|a - b| = X,不需要修改(0次) - 如果可以通过修改其中一个元素达到差值 X,需要1次修改
- 否则需要修改两个元素,需要2次修改
- 如果
优化计算:使用差分数组技巧来高效计算所有可能 X 值对应的修改次数。
关键观察:
- 对于一对
(a, b),如果要达到差值 X 且只修改一个元素,那么修改后的值必须在 [0, k] 范围内 - 可以通过分析每对元素对不同 X 值的贡献,使用差分数组批量更新
算法步骤:
- 计算每对元素的当前差值
- 对于每对,分析在什么 X 值范围内需要 0/1/2 次修改
- 使用差分数组技巧统计每个 X 值对应的总修改次数
- 返回最小修改次数
代码实现
class Solution {
public:
int minChanges(vector<int>& nums, int k) {
int n = nums.size();
vector<int> diff(k + 2, 0); // 差分数组
int totalPairs = n / 2;
for (int i = 0; i < n / 2; i++) {
int a = nums[i];
int b = nums[n - 1 - i];
int currentDiff = abs(a - b);
// 当前差值不需要修改
diff[currentDiff]--;
diff[currentDiff + 1]++;
// 计算只需要修改一个元素就能达到的差值范围
int maxPossible = max({a, b, k - a, k - b});
// 差值在 [0, maxPossible] 范围内只需要修改一个元素
// 超出这个范围需要修改两个元素
diff[maxPossible + 1]++;
}
// 应用差分数组,计算每个X值对应的修改次数
int changes = totalPairs * 2; // 初始假设所有对都需要修改2次
int result = changes;
for (int x = 0; x <= k; x++) {
changes += diff[x];
result = min(result, changes);
}
return result;
}
};
class Solution:
def minChanges(self, nums: List[int], k: int) -> int:
n = len(nums)
diff = [0] * (k + 2) # 差分数组
total_pairs = n // 2
for i in range(n // 2):
a, b = nums[i], nums[n - 1 - i]
current_diff = abs(a - b)
# 当前差值不需要修改
diff[current_diff] -= 1
if current_diff + 1 <= k:
diff[current_diff + 1] += 1
# 计算只需要修改一个元素就能达到的最大差值
max_possible = max(a, b, k - a, k - b)
# 超出最大可能差值需要修改两个元素
if max_possible + 1 <= k:
diff[max_possible + 1] += 1
# 应用差分数组,计算每个X值对应的修改次数
changes = total_pairs * 2 # 初始假设所有对都需要修改2次
result = changes
for x in range(k + 1):
changes += diff[x]
result = min(result, changes)
return result
public class Solution {
public int MinChanges(int[] nums, int k) {
int n = nums.Length;
int[] diff = new int[k + 2]; // 差分数组
int totalPairs = n / 2;
for (int i = 0; i < n / 2; i++) {
int a = nums[i];
int b = nums[n - 1 - i];
int currentDiff = Math.Abs(a - b);
// 当前差值不需要修改
diff[currentDiff]--;
if (currentDiff + 1 <= k) {
diff[currentDiff + 1]++;
}
// 计算只需要修改一个元素就能达到的最大差值
int maxPossible = Math.Max(Math.Max(a, b), Math.Max(k - a, k - b));
// 超出最大可能差值需要修改两个元素
if (maxPossible + 1 <= k) {
diff[maxPossible + 1]++;
}
}
// 应用差分数组,计算每个X值对应的修改次数
int changes = totalPairs * 2; // 初始假设所有对都需要修改2次
int result = changes;
for (int x = 0; x <= k; x++) {
changes += diff[x];
result = Math.Min(result, changes);
}
return result;
}
}
var minChanges = function(nums, k) {
const n = nums.length;
const diff = new Array(k + 2).fill(0); // 差分数组
const totalPairs = Math.floor(n / 2);
for (let i = 0; i < Math.floor(n / 2); i++) {
const a = nums[i];
const b = nums[n - 1 - i];
const currentDiff = Math.abs(a - b);
// 当前差值不需要修改
diff[currentDiff]--;
if (currentDiff + 1 <= k) {
diff[currentDiff + 1]++;
}
// 计算只需要修改一个元素就能达到的最大差值
const maxPossible = Math.max(a, b, k - a, k - b);
// 超出最大可能差值需要修改两个元素
if (maxPossible + 1 <= k) {
diff[maxPossible + 1]++;
}
}
// 应用差分数组,计算每个X值对应的修改次数
let changes = totalPairs * 2; // 初始假设所有对都需要修改2次
let result = changes;
for (let x = 0; x <= k; x++) {
changes += diff[x];
result = Math.min(result, changes);
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n + k) | 遍历数组一次 O(n),应用差分数组 O(k) |
| 空间复杂度 | O(k) | 差分数组的空间开销 |