leetcode
leetcode 1651 ~ 1700
邻位交换的最小次数

邻位交换的最小次数

难度:

标签:

题目描述

代码结果

运行时间: 116 ms, 内存: 17.4 MB


/*
 * Similar to the traditional Java solution, we can use Java streams
 * to manipulate and find permutations, and then determine the required swaps.
 */

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

public class SolutionStream {
    public int getMinSwaps(String num, int k) {
        char[] chars = num.toCharArray();
        String target = IntStream.range(0, k).mapToObj(i -> {
            nextPermutation(chars);
            return new String(chars);
        }).reduce((first, second) -> second).orElse(num);

        chars = num.toCharArray();
        int swaps = 0;

        for (int i = 0; i < num.length(); i++) {
            if (chars[i] != target.charAt(i)) {
                int j = i;
                while (chars[j] != target.charAt(i)) {
                    j++;
                }
                while (j > i) {
                    swap(chars, j, j - 1);
                    j--;
                    swaps++;
                }
            }
        }

        return swaps;
    }

    private void nextPermutation(char[] chars) {
        int i = chars.length - 2;
        while (i >= 0 && chars[i] >= chars[i + 1]) {
            i--;
        }
        if (i >= 0) {
            int j = chars.length - 1;
            while (chars[j] <= chars[i]) {
                j--;
            }
            swap(chars, i, j);
        }
        reverse(chars, i + 1);
    }

    private void swap(char[] chars, int i, int j) {
        char temp = chars[i];
        chars[i] = chars[j];
        chars[j] = temp;
    }

    private void reverse(char[] chars, int start) {
        int end = chars.length - 1;
        while (start < end) {
            swap(chars, start, end);
            start++;
            end--;
        }
    }
}

解释

方法:

解决这个问题涉及两个主要步骤:1. 找到给定数字字符串的第k个字典序更大的排列。2. 计算从原始字符串到该排列通过相邻交换得到的最小次数。首先,使用'kthNextPermutation'函数来获取所需的排列,此函数实现了对给定数组的下一个排列算法,并能够处理有重复数字的情况。之后,利用索引映射和树状数组(Binary Indexed Tree, BIT),来计算把原数组转换到目标排列需要的最小交换次数。树状数组用于高效计算前缀和,以此来快速得出每次交换的次数。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在`kthNextPermutation`函数中,如何确保找到的排列是第k个字典序更大的排列而非仅仅是下一个?
在`kthNextPermutation`函数中,通过多次应用下一个字典序排列的算法来确保找到第k个字典序更大的排列。具体做法是,从给定排列开始,反复调用一次计算下一个排列的函数,重复这个过程k次。每次调用都是基于当前排列找到字典序中的下一个排列,通过连续执行k次,最终达到第k个更大的排列。
🦆
函数中使用到了树状数组(BIT),为什么选择这种数据结构而不是其他例如线段树或简单数组?
树状数组(BIT)被选用是因为它在处理频繁的单点更新和区间求和查询时提供了较为高效的性能,且实现相比线段树更为简洁。在计算最小交换次数过程中,需要频繁地更新单个元素并查询区间和,BIT能够以对数时间复杂度处理这些操作,而简单数组处理区间求和时会更慢。相对于线段树,BIT在空间上更加节省且足够应对此问题的需求。
🦆
在计算最小交换次数时,为什么需要使用索引映射和树状数组来计算前缀和?能否简单解释其原理和步骤?
计算最小交换次数时,使用索引映射和树状数组可以高效地确定每个数字应该移动到的位置并计算必要的交换次数。具体步骤如下:首先,通过索引映射确定每个数字在目标排列中的位置。然后,使用树状数组来维护和查询已经放置在最终位置上的数字的数量。对于每个数字,通过查询树状数组可以快速得到在当前数字前已经正确放置的数字数量,从而计算出将当前数字移动到正确位置需要的交换次数。每处理一个数字后,更新树状数组以反映这一变化。
🦆
能否详细解释一下`al`数组在解决方案中的作用是什么以及它是如何构建的?
`al`数组在解决方案中的作用是记录每个数字在目标排列中的位置。它是这样构建的:遍历原始数列的每个数字,对于每个数字,使用索引映射(由目标排列定义)找到该数字在目标排列中应出现的位置,并将这些位置添加到`al`数组中。这个位置数组随后用于树状数组中,以帮助计算和跟踪每个数字到达其目标位置需要进行的交换次数。

相关问题