青训营X豆包MarsCode 刷题 乱序列表升序问题 | 豆包MarsCode AI刷题

75 阅读3分钟

问题描述

小R有一个长度为n的排列,排列中的数字是1到n的整数。她每次操作可以选择两个数ai和aj进行交换,前提是这两个数的下标i和」的奇偶性相同(即同为奇数或同为偶数)。小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

题目分析: 这个问题要求我们通过最少的交换操作,将一个排列 a(包含数字 1 到 n 的整数)变成升序排列,操作的条件是交换的两个数 a[i]a[j] 必须满足它们的下标 ij 是奇偶性相同的,即两个下标要么都为奇数,要么都为偶数。

首先这道题最基础的部分是:把一个乱序的列表通过元素之间的两两交换实现元素的升序 统一的结论就是: 将这个位置应该出现的元素和这个位置现在的元素交换位置

这里可以通过建立字典进行实现

第二部分就是判断当前列表能否通过两两交换实现升序,对于1-n的数字来说奇数一定要在024下标位置上,偶数一定要在135下标位置上,所以我们通过判段一种即可实现判定。

因此最终在计算交换次数的时候是分为两部分来计算的:将原数组的奇数下标设为一个新的数组、偶数下表设为一个新的数组,最终两个数字进行求和即可。

第一部分的核心代码:

# 记录每个元素的原始索引位置
hashmap = {num: i for i, num in enumerate(nums)}
print(hashmap)

# 遍历数组,进行交换
for i in range(len(nums)):
    if nums[i] != i + 1:  # 当前位置应该是 i+1
        # 找到 i+1 在原数组中的索引
        index = hashmap[i + 1]
        
        # 交换 nums[i] 和 nums[index]
        nums[i], nums[index] = nums[index], nums[i]
        
        result += 1
        

第二部分的核心代码:

for i in range(n):
    if nums[0] % 2 == 1:
        while nums[i] != 2* i + 1:  # 当前位置应该是 i+1
            index = hashmap[2* i + 1]  # 找到 i+1 在原数组中的索引
            # 交换 nums[i] 和 nums[index]
            nums[i], nums[index] = nums[index], nums[i]
            
            # 更新 hashmap 中的索引信息
            hashmap[nums[i]] = i
            hashmap[nums[index]] = index
            
            result += 1
  • 时间复杂度O(n log n)。由于我们需要分别对奇数位置和偶数位置的数组进行排序,并且计算逆序对的时间复杂度为 O(n log n),所以总的时间复杂度为 O(n log n)

  • 空间复杂度O(n)。我们需要额外的空间来存储临时数组和计算排序。