问题描述
小R有一个长度为 n 的排列,排列中的数字是 1 到 n 的整数。她每次操作可以选择两个数 a_i 和 a_j 进行交换,前提是这两个数的下标 i 和 j 的奇偶性相同(即同为奇数或同为偶数)。小R希望通过最少的操作使数组变成升序排列。
请你帮小R计算,最少需要多少次操作才能使得数组有序。如果不能通过这样的操作使数组有序,则输出 -1。
测试样例
样例1:
输入:
n = 5, a = [1, 4, 5, 2, 3]
输出:2
样例2:
输入:
n = 4, a = [4, 3, 2, 1]
输出:-1
样例3:
输入:
n = 6, a = [2, 4, 6, 1, 3, 5]
输出:-1
问题分析
小R的任务是将一个长度为 n 的排列,通过交换某些元素,变成一个升序排列。排列中的数字是 1 到 n 的整数,每次交换的前提是只能交换下标同为奇数或同为偶数的元素。我们需要计算最少需要多少次交换操作才能使得数组有序,如果无法完成,则返回 -1。
问题关键点:
- 只能交换下标同为奇数或同为偶数的元素。
- 数组是否可以通过满足上述交换条件来排序为升序排列。
解题思路
-
分类奇数与偶数下标:
-
将数组中的元素按照下标的奇偶性进行分类:
- 偶数下标的元素放在一组(
even_indices)。 - 奇数下标的元素放在另一组(
odd_indices)。
- 偶数下标的元素放在一组(
-
-
单独排序每个部分:
- 如果数组能够通过满足条件的交换来实现升序排序,那么在各自奇偶性下标的元素独立排序后,合并结果也应该是升序排列。
- 我们将这两部分分别排序,然后检查合并后的顺序是否能形成完整的升序。
-
计算最少的交换次数:
- 使用贪心策略来计算最少的交换次数,使得奇数和偶数下标的元素排成升序。
- 如果不能实现升序,则直接返回
-1。
实现步骤
- 将数组中的元素按下标的奇偶性进行分组。
- 分别对奇数下标和偶数下标的元素排序。
- 合并两个排序后的数组,检查合并后的顺序是否为升序。
- 如果可以排序,计算最少交换次数;如果不能,返回
-1。 解题代码
def solution(n, a):
odd_indices = [a[i] for i in range(n) if i % 2 == 1]
even_indices = [a[i] for i in range(n) if i % 2 == 0]
odd_sorted = sorted(odd_indices)
even_sorted = sorted(even_indices)
merged_array = []
odd_idx, even_idx = 0, 0
for i in range(n):
if i % 2 == 0:
merged_array.append(even_sorted[even_idx])
even_idx += 1
else:
merged_array.append(odd_sorted[odd_idx])
odd_idx += 1
for i in range(1, n):
if merged_array[i] < merged_array[i - 1]:
return -1
def min_swaps(arr):
sorted_arr = sorted(arr)
visited = [False] * len(arr)
swap_count = 0
for i in range(len(arr)):
if visited[i] or arr[i] == sorted_arr[i]:
continue
cycle_size = 0
j = i
while not visited[j]:
visited[j] = True
j = arr.index(sorted_arr[j])
cycle_size += 1
if cycle_size > 1:
swap_count += (cycle_size - 1)
return swap_count
odd_swaps = min_swaps(odd_indices)
even_swaps = min_swaps(even_indices)
return odd_swaps + even_swaps
代码解释
-
拆分奇数和偶数位置的元素:
odd_indices:存储原数组中所有奇数下标位置的元素。even_indices:存储原数组中所有偶数下标位置的元素。
-
对奇数和偶数位置的元素分别进行排序:
- 对这两个部分分别排序,使它们各自处于升序状态。
-
合并已排序的奇数和偶数位置的元素并检查是否有序:
- 合并后,如果整个数组不满足升序条件,则输出
-1。 - 逐一比较合并后的数组,检查是否满足升序条件。
- 合并后,如果整个数组不满足升序条件,则输出
-
最少交换次数的计算:
-
为了使每个部分(奇数下标和偶数下标)都排序,我们使用一个辅助函数
min_swaps来计算最少的交换次数。 -
min_swaps函数: 通过遍历原数组与排序后的数组来找到交换的环,并计算环的大小,最终得到最少的交换次数。 如果环的大小大于 1,那么交换次数等于环的大小减 1。
-
-
返回结果:
- 如果可以排序,返回奇数和偶数部分的最少交换次数之和;否则返回
-1。
- 如果可以排序,返回奇数和偶数部分的最少交换次数之和;否则返回
时间复杂度
-
时间复杂度:
O(n log n),主要来自于对奇数和偶数位置的元素进行排序。- 计算最少交换次数的时间复杂度为
O(n),而排序的时间复杂度为O(n log n),因此总体时间复杂度为O(n log n)。
- 计算最少交换次数的时间复杂度为
-
空间复杂度:
O(n),需要额外的空间来存储奇数和偶数位置的元素及辅助数组。