Medium
题目描述
给你一个由不同整数组成的数组 nums。
在一次操作中,你可以交换数组中任意两个相邻的元素。
如果数组的一种排列中相邻元素的奇偶性交替出现,即每对相邻元素都由一个偶数和一个奇数组成,则该排列被认为是有效的。
返回将 nums 转换为任意有效排列所需的最小相邻交换次数。
如果无法重新排列 nums 使得没有两个相邻元素具有相同的奇偶性,则返回 -1。
示例 1:
输入:nums = [2,4,6,5,7]
输出:3
解释:
交换 5 和 6,数组变为 [2,4,5,6,7]
交换 5 和 4,数组变为 [2,5,4,6,7]
交换 6 和 7,数组变为 [2,5,4,7,6]。数组现在是有效排列。因此答案是 3。
示例 2:
输入:nums = [2,4,5,7]
输出:1
解释:
交换 4 和 5,数组变为 [2,5,4,7],这是一个有效排列。因此答案是 1。
示例 3:
输入:nums = [1,2,3]
输出:0
解释:
数组已经是有效排列。因此不需要任何操作。
示例 4:
输入:nums = [4,5,6,8]
输出:-1
解释:
不可能有有效排列。因此答案是 -1。
约束条件:
1 <= nums.length <= 10^51 <= nums[i] <= 10^9nums中所有元素都不相同
解题思路
这道题要求通过相邻交换使数组的奇偶性交替排列。我们需要分析两种可能的目标模式:以偶数开头或以奇数开头。
核心思路:
可行性检查:首先统计偶数和奇数的个数。如果它们的个数差值大于1,则无法形成交替排列,返回-1。
模式分析:
- 如果数组长度为奇数,只能选择数量较多的那种数字作为开头
- 如果数组长度为偶数,两种模式都可能,需要都尝试并取最小值
贪心策略:对于确定的目标模式,我们需要将当前的偶数/奇数移动到目标位置。关键观察是,通过相邻交换,将一个元素从位置i移动到位置j的最小交换次数就是|i-j|。
计算交换次数:
- 收集所有偶数的当前位置和目标位置
- 收集所有奇数的当前位置和目标位置
- 分别计算移动成本并求和
算法步骤:
- 统计偶数和奇数个数,判断可行性
- 根据数组长度确定需要尝试的模式
- 对每种模式,计算偶数和奇数的移动成本
- 返回最小成本
代码实现
class Solution {
public:
int minSwaps(vector<int>& nums) {
int n = nums.size();
int evenCnt = 0, oddCnt = 0;
for (int num : nums) {
if (num % 2 == 0) evenCnt++;
else oddCnt++;
}
if (abs(evenCnt - oddCnt) > 1) return -1;
auto calculateCost = [&](bool evenFirst) -> int {
vector<int> evenPos, oddPos;
vector<int> evenTarget, oddTarget;
for (int i = 0; i < n; i++) {
if (nums[i] % 2 == 0) {
evenPos.push_back(i);
} else {
oddPos.push_back(i);
}
}
for (int i = 0; i < n; i++) {
if ((i % 2 == 0) == evenFirst) {
evenTarget.push_back(i);
} else {
oddTarget.push_back(i);
}
}
int cost = 0;
for (int i = 0; i < evenPos.size(); i++) {
cost += abs(evenPos[i] - evenTarget[i]);
}
for (int i = 0; i < oddPos.size(); i++) {
cost += abs(oddPos[i] - oddTarget[i]);
}
return cost;
};
if (n % 2 == 1) {
return calculateCost(evenCnt > oddCnt);
} else {
return min(calculateCost(true), calculateCost(false));
}
}
};
class Solution:
def minSwaps(self, nums: List[int]) -> int:
n = len(nums)
even_cnt = sum(1 for num in nums if num % 2 == 0)
odd_cnt = n - even_cnt
if abs(even_cnt - odd_cnt) > 1:
return -1
def calculate_cost(even_first):
even_pos = [i for i, num in enumerate(nums) if num % 2 == 0]
odd_pos = [i for i, num in enumerate(nums) if num % 2 == 1]
even_target = [i for i in range(n) if (i % 2 == 0) == even_first]
odd_target = [i for i in range(n) if (i % 2 == 0) != even_first]
cost = 0
for i in range(len(even_pos)):
cost += abs(even_pos[i] - even_target[i])
for i in range(len(odd_pos)):
cost += abs(odd_pos[i] - odd_target[i])
return cost
if n % 2 == 1:
return calculate_cost(even_cnt > odd_cnt)
else:
return min(calculate_cost(True), calculate_cost(False))
public class Solution {
public int MinSwaps(int[] nums) {
int n = nums.Length;
int evenCnt = 0, oddCnt = 0;
foreach (int num in nums) {
if (num % 2 == 0) evenCnt++;
else oddCnt++;
}
if (Math.Abs(evenCnt - oddCnt) > 1) return -1;
int CalculateCost(bool evenFirst) {
var evenPos = new List<int>();
var oddPos = new List<int>();
var evenTarget = new List<int>();
var oddTarget = new List<int>();
for (int i = 0; i < n; i++) {
if (nums[i] % 2 == 0) {
evenPos.Add(i);
} else {
oddPos.Add(i);
}
}
for (int i = 0; i < n; i++) {
if ((i % 2 == 0) == evenFirst) {
evenTarget.Add(i);
} else {
oddTarget.Add(i);
}
}
int cost = 0;
for (int i = 0; i < evenPos.Count; i++) {
cost += Math.Abs(evenPos[i] - evenTarget[i]);
}
for (int i = 0; i < oddPos.Count; i++) {
cost += Math.Abs(oddPos[i] - oddTarget[i]);
}
return cost;
}
if (n % 2 == 1) {
return CalculateCost(evenCnt > oddCnt);
} else {
return Math.Min(CalculateCost(true), CalculateCost(false));
}
}
}
var minSwaps = function(nums) {
const n = nums.length;
const evens = [];
const odds = [];
for (let i = 0; i < n; i++) {
if (nums[i] % 2 === 0) {
evens.push(i);
} else {
odds.push(i);
}
}
if (Math.abs(evens.length - odds.length) > 1) {
return -1;
}
function countSwaps(firstType, firstIndices, secondIndices) {
let swaps = 0;
let arr = [...nums];
for (let i = 0; i < n; i++) {
const needEven = (firstType === 'even' && i % 2 === 0) || (firstType === 'odd' && i % 2 === 1);
if ((arr[i] % 2 === 0) !== needEven) {
let j = i + 1;
while (j < n && (arr[j] % 2 === 0) === (arr[i] % 2 === 0)) {
j++;
}
while (j > i) {
[arr[j], arr[j-1]] = [arr[j-1], arr[j]];
swaps++;
j--;
}
}
}
return swaps;
}
let result = Infinity;
if (evens.length >= odds.length) {
result = Math.min(result, countSwaps('even', evens, odds));
}
if (odds.length >= evens.length) {
result = Math.min(result, countSwaps('odd', odds, evens));
}
return result;
};
复杂度分析
| 复杂度类型 | 大小 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |
说明:
- 时间复杂度:遍历数组统计奇偶数个数需要O(n),计算每种模式的成本也需要O(n),最多计算2种模式,总体为O(n)
- 空间复杂度:需要存储位置数组,最坏情况下需要O(n)的额外空间