leetcode
leetcode 51 ~ 100
排列序列

排列序列

难度:

标签:

题目描述

给出集合 [1,2,3,...,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

给定 n 和 k,返回第 k 个排列。

 

示例 1:

输入:n = 3, k = 3
输出:"213"

示例 2:

输入:n = 4, k = 9
输出:"2314"

示例 3:

输入:n = 3, k = 1
输出:"123"

 

提示:

  • 1 <= n <= 9
  • 1 <= k <= n!

代码结果

运行时间: 43 ms, 内存: 15.9 MB


/*
 * 题目思路:
 * 1. 使用 Stream API 初始化候选数字的列表。
 * 2. 使用循环选择每一位数字,按照与常规 Java 版本相同的逻辑。
 * 3. 使用流操作来生成结果字符串。
 */
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class Solution {
    public String getPermutation(int n, int k) {
        int[] factorial = new int[n];
        List<Integer> numbers = IntStream.rangeClosed(1, n).boxed().collect(Collectors.toList());
        StringBuilder result = new StringBuilder();
 
        // 计算阶乘数组
        factorial[0] = 1;
        for (int i = 1; i < n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }
 
        k--;
        for (int i = n; i > 0; i--) {
            int index = k / factorial[i - 1];
            result.append(numbers.remove(index));
            k %= factorial[i - 1];
        }
 
        return result.toString();
    }
}

解释

方法:

这个题解使用了数学推导的方法,通过计算阶乘数组和确定每一位数字来生成第k个排列。具体步骤如下: 1. 计算阶乘数组 factorial,其中 factorial[i] 表示 i 的阶乘,用于后续计算每一位数字的索引。 2. 初始化剩余数字列表 nums,包含从 1 到 n 的所有数字。 3. 初始化结果字符串 result,用于存储最终的排列结果。 4. 从最高位开始,通过 k 除以 factorial[i] 的商确定每一位的数字,并将对应的数字从 nums 中移除,同时更新 k 的值。 5. 重复步骤4,直到确定了所有位的数字。 6. 返回生成的排列结果 result。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中,如何确定每一位数字的具体值?请详细解释这个过程。
在题解中,确定每一位数字的具体值的过程是通过计算 k 除以 factorial[i] 的商来获得当前位的索引。这里,factorial[i] 表示第 i+1 位数字后所有数字的排列组合数。索引用于从剩余数字列表 nums 中选择数字。例如,若索引为 2,则从 nums 中选择第三个数字。通过这种方法,可以根据 k 的值逐步确定每一位上的数字,直到构成完整的排列。
🦆
为什么在计算每一位数字时需要将 k 减去 'index * factorial[i]',这样做的目的是什么?
在计算每一位数字时,将 k 减去 'index * factorial[i]' 的目的是为了更新 k 的值,以便能正确计算下一位数字的索引。这个减法操作实际上是移除了当前确定的数字所代表的所有排列组合数量,因此更新后的 k 表示在剩余数字中,所需查找的新排列的相对位置。
🦆
题解中提到将数字从剩余数字列表 nums 中移除,这个操作为何重要,它对生成正确的排列结果有什么影响?
将数字从剩余数字列表 nums 中移除是非常重要的操作,因为每选择一个数字作为排列的一部分后,这个数字就不应再被重复使用。此操作确保了每个数字在最终排列中只出现一次,遵循排列的定义(即每个数字唯一且仅用一次)。如果不移除已选数字,可能会导致重复使用相同的数字,进而生成错误的排列结果。
🦆
题解中使用了阶乘数组 factorial,这种预处理的意义何在?
阶乘数组 factorial 的预处理主要是为了快速计算在任意位置上可能的排列数。每个 factorial[i] 存储的是从 i+1 到 n 的所有数字的排列数,即 (n-i)!。这样的预处理允许算法快速确定每个位置上的索引,从而有效地计算出每一步的数字选择。这种方法避免了重复计算阶乘,提高了整体算法的效率。

相关问题

下一个排列

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

 

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

 

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

 

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

 

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同