leetcode
leetcode 2051 ~ 2100
生成有效数组的最少交换次数

生成有效数组的最少交换次数

难度:

标签:

题目描述

代码结果

运行时间: 47 ms, 内存: 28.7 MB


/*
 * Problem: Minimum Swaps to Make Array Increasing
 * Given an array nums, you are allowed to swap any two elements in the array.
 * Return the minimum number of swaps needed to make the array strictly increasing.
 * 
 * Approach:
 * 1. Convert the array to a stream and sort it.
 * 2. Map each element to its index in the sorted array.
 * 3. Calculate the minimum swaps by finding cycles in the mapped indices.
 */

import java.util.*;
import java.util.stream.*;

public class MinimumSwapsStream {
    public int minSwaps(int[] nums) {
        int n = nums.length;
        int[] sortedNums = Arrays.stream(nums).sorted().toArray();
        Map<Integer, Integer> numToIndex = IntStream.range(0, n).boxed().collect(Collectors.toMap(i -> sortedNums[i], i -> i));
        boolean[] visited = new boolean[n];
        int swaps = 0;

        for (int i = 0; i < n; i++) {
            if (visited[i] || nums[i] == sortedNums[i]) continue;

            int cycleSize = 0;
            int x = i;
            while (!visited[x]) {
                visited[x] = true;
                x = numToIndex.get(nums[x]);
                cycleSize++;
            }

            if (cycleSize > 0) {
                swaps += (cycleSize - 1);
            }
        }

        return swaps;
    }

    public static void main(String[] args) {
        MinimumSwapsStream solution = new MinimumSwapsStream();
        int[] nums = {4, 3, 2, 1};
        System.out.println(solution.minSwaps(nums)); // Output should be 2
    }
}

解释

方法:

这个解法首先遍历一次数组,找出数组中的最小值和最大值以及它们的索引。最小值的索引记为 idx1,最大值的索引记为 idx2。这个解法的关键在于通过计算最小值和最大值的位置来确定最少的交换次数:1. 如果最小值在最大值之前出现(idx1 <= idx2),则直接计算两者到其应在的位置的距离差,即 idx1 (最小值移动到数组起始位置) 和 n-1-idx2 (最大值移动到数组末尾位置)。2. 如果最小值在最大值之后出现(idx1 > idx2),需要额外减去1,因为当最小值向前移动时,最大值的位置也会随之前移。这种情况下,我们在计算最大值移动到末尾的距离时多算了一次。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在最小值在最大值后面出现时需要额外减去1?具体是如何影响交换次数的?
在最小值在最大值后面的情况下,当最小值向数组的起始位置移动时,如果不减去1,则会重复计算最大值的位置移动。因为在将最小值向前移动过程中,最大值的索引会因为数组的改变而自动向前移动一位,所以在计算最大值向数组末尾移动的距离时,应该减去这个已经因最小值移动而产生的一次位移,以避免重复计数。
🦆
在遍历数组时,对于相同的最小值或最大值,选择更新索引的策略是什么?为什么最大值选择了`>=`而不仅是`>`?
在遍历数组时,如果遇到值等于当前最小值的情况,通常保持最小值的索引为首次出现的位置,因为这有助于减少总的交换次数,即尽可能保持最小值靠近开始位置。对于最大值,选择`>=`而不是`>`是为了在遇到相同的最大值时更新索引到最后出现的位置,这样可以减少最大值向数组末尾移动的距离,从而可能减少总的交换次数。
🦆
这种计算最少交换次数的方法是否考虑了数组中可能存在的重复元素?重复元素会怎样影响结果?
这种方法在计算时确实考虑了数组中可能存在的重复元素,因为它通过更新最小值和最大值的索引来应对重复出现的情况。重复元素本身不会影响结果的正确性,因为算法在考虑最少交换次数时已经考虑到了最小值和最大值可能多次出现的情形。
🦆
如果数组已经是按照最小值到最大值排序的,这个算法是否还有效?它会返回正确的交换次数吗?
如果数组已经是按照最小值到最大值排序的,则最小值的索引为0,最大值的索引为数组长度减一。根据算法逻辑,这种情况下返回的交换次数将是0,这是正确的结果,因为已排序数组不需要任何交换。因此,这个算法在这种情况下仍然有效并且能够返回正确的交换次数。

相关问题