AI 刷题 213. 小R的排列冒险 题解 | 豆包MarsCode AI刷题

49 阅读4分钟

问题描述

小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 的排列,通过交换某些元素,变成一个升序排列。排列中的数字是 1n 的整数,每次交换的前提是只能交换下标同为奇数或同为偶数的元素。我们需要计算最少需要多少次交换操作才能使得数组有序,如果无法完成,则返回 -1

问题关键点

  • 只能交换下标同为奇数或同为偶数的元素。
  • 数组是否可以通过满足上述交换条件来排序为升序排列。

解题思路

  1. 分类奇数与偶数下标

    • 将数组中的元素按照下标的奇偶性进行分类:

      • 偶数下标的元素放在一组(even_indices)。
      • 奇数下标的元素放在另一组(odd_indices)。
  2. 单独排序每个部分

    • 如果数组能够通过满足条件的交换来实现升序排序,那么在各自奇偶性下标的元素独立排序后,合并结果也应该是升序排列。
    • 我们将这两部分分别排序,然后检查合并后的顺序是否能形成完整的升序。
  3. 计算最少的交换次数

    • 使用贪心策略来计算最少的交换次数,使得奇数和偶数下标的元素排成升序。
    • 如果不能实现升序,则直接返回 -1

实现步骤

  1. 将数组中的元素按下标的奇偶性进行分组。
  2. 分别对奇数下标和偶数下标的元素排序。
  3. 合并两个排序后的数组,检查合并后的顺序是否为升序。
  4. 如果可以排序,计算最少交换次数;如果不能,返回 -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


代码解释

  1. 拆分奇数和偶数位置的元素

    • odd_indices:存储原数组中所有奇数下标位置的元素。
    • even_indices:存储原数组中所有偶数下标位置的元素。
  2. 对奇数和偶数位置的元素分别进行排序

    • 对这两个部分分别排序,使它们各自处于升序状态。
  3. 合并已排序的奇数和偶数位置的元素并检查是否有序

    • 合并后,如果整个数组不满足升序条件,则输出 -1
    • 逐一比较合并后的数组,检查是否满足升序条件。
  4. 最少交换次数的计算

    • 为了使每个部分(奇数下标和偶数下标)都排序,我们使用一个辅助函数 min_swaps 来计算最少的交换次数。

    • min_swaps 函数: 通过遍历原数组与排序后的数组来找到交换的环,并计算环的大小,最终得到最少的交换次数。 如果环的大小大于 1,那么交换次数等于环的大小减 1。

  5. 返回结果

    • 如果可以排序,返回奇数和偶数部分的最少交换次数之和;否则返回 -1

时间复杂度

  • 时间复杂度O(n log n),主要来自于对奇数和偶数位置的元素进行排序。

    • 计算最少交换次数的时间复杂度为 O(n),而排序的时间复杂度为 O(n log n),因此总体时间复杂度为 O(n log n)
  • 空间复杂度O(n),需要额外的空间来存储奇数和偶数位置的元素及辅助数组。