leetcode
leetcode 2301 ~ 2350
半有序排列

半有序排列

难度:

标签:

题目描述

You are given a 0-indexed permutation of n integers nums.

A permutation is called semi-ordered if the first number equals 1 and the last number equals n. You can perform the below operation as many times as you want until you make nums a semi-ordered permutation:

  • Pick two adjacent elements in nums, then swap them.

Return the minimum number of operations to make nums a semi-ordered permutation.

A permutation is a sequence of integers from 1 to n of length n containing each number exactly once.

 

Example 1:

Input: nums = [2,1,4,3]
Output: 2
Explanation: We can make the permutation semi-ordered using these sequence of operations: 
1 - swap i = 0 and j = 1. The permutation becomes [1,2,4,3].
2 - swap i = 2 and j = 3. The permutation becomes [1,2,3,4].
It can be proved that there is no sequence of less than two operations that make nums a semi-ordered permutation. 

Example 2:

Input: nums = [2,4,1,3]
Output: 3
Explanation: We can make the permutation semi-ordered using these sequence of operations:
1 - swap i = 1 and j = 2. The permutation becomes [2,1,4,3].
2 - swap i = 0 and j = 1. The permutation becomes [1,2,4,3].
3 - swap i = 2 and j = 3. The permutation becomes [1,2,3,4].
It can be proved that there is no sequence of less than three operations that make nums a semi-ordered permutation.

Example 3:

Input: nums = [1,3,4,2,5]
Output: 0
Explanation: The permutation is already a semi-ordered permutation.

 

Constraints:

  • 2 <= nums.length == n <= 50
  • 1 <= nums[i] <= 50
  • nums is a permutation.

代码结果

运行时间: 27 ms, 内存: 16.0 MB


/*
 * 题目思路:
 * 要将数组变为半有序排列,必须满足以下条件:
 * 1. 第一个元素为1。
 * 2. 最后一个元素为n。
 * 需要统计1的位置和n的位置,然后计算它们需要移动的步数。
 * 如果1的位置在前,n的位置在后,中间位置无关紧要。
 * 使用Java Stream API实现
 */
import java.util.stream.IntStream;
public class Solution {
    public int semiOrderedPermutation(int[] nums) {
        int n = nums.length;
        int firstIndex = IntStream.range(0, n).filter(i -> nums[i] == 1).findFirst().getAsInt();
        int lastIndex = IntStream.range(0, n).filter(i -> nums[i] == n).findFirst().getAsInt();
        int steps1 = firstIndex;
        int stepsn = n - 1 - lastIndex;
        return firstIndex > lastIndex ? steps1 + stepsn - 1 : steps1 + stepsn;
    }
}

解释

方法:

该题解的核心思路是通过计算将数字1和数字n移动到序列的首尾所需的最小交换次数。首先,找到数字1 (firstIdx) 和数字n (lastIdx) 在序列中的位置。如果数字1在数字n之前(firstIdx < lastIdx),则我们只需要分别将这两个数字移动到序列的两端。如果数字n在数字1之前(firstIdx > lastIdx),则在移动的同时需要多执行一次交换(omni为1),这是因为当1在n的右边时,将它们移到两端后,它们的相对位置仍然需要调整。最终的最小操作次数计算为firstIdx加上从lastIdx到序列末尾的距离,再减去omni。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在实现中,为什么选择用索引位置来计算交换次数,而不是直接操作数组本身进行交换?
使用索引位置来计算交换次数而不直接操作数组的原因是效率和简化问题的复杂性。直接操作数组进行交换会涉及到实际的数组元素移动,这可能导致算法的时间复杂度增加,并且在实现上更为复杂。而通过计算索引位置的偏移,我们可以直接得到需要的交换次数,这样做既可以减少计算量,也使得算法更加清晰易懂。此外,这种方法也便于理论分析和算法的证明。
🦆
算法中提到的`omni`变量是如何确定需要的额外交换次数的?能否通过一个具体的例子来解释这一点?
在算法中,`omni`变量用于处理特殊情况,即当数字1在数字n的右侧时(即`firstIdx > lastIdx`),这意味着数字1和数字n在最终的半有序排列中不仅要移动到数组的两端,还需要调整它们的相对位置。例如,对于数组`[2, 3, 1, 4]`,数字1的索引是2,数字4(n)的索引是3。在将1移动到数组的开始,将4移动到数组的末尾后,我们发现在排序过程中,1和4的相对位置需要额外的一次交换才能正确地放置。因此,`omni`被设置为1,以表示这种额外的交换需求。
🦆
当数字1和数字n的索引相同时(firstIdx == lastIdx),算法将如何处理?
当数字1和数字n的索引相同时,这意味着1和n是同一个数字,即数组长度为1,且只包含一个元素`1`。在这种特殊情况下,数组实际上已经是一个半有序排列,因为它的首尾元素分别是1和n。因此,不需要任何交换操作,操作次数为0。这种情况在算法中是隐含处理的,因为如果数组仅有一个元素,那么`firstIdx`和`lastIdx`都为0,且`omni`不会增加任何额外的交换次数,结果自然为0。

相关问题