0060. Permutation Sequence (M)
Permutation Sequence (M)
题目
The set [1,2,3,...,*n*]
contains a total of n! unique permutations.
By listing and labeling all of the permutations in order, we get the following sequence for n = 3:
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note:
- Given n will be between 1 and 9 inclusive.
- Given k will be between 1 and n! inclusive.
Example 1:
Input: n = 3, k = 3
Output: "213"
Example 2:
Input: n = 3, k = 3
Output: "213"
题意
按字典序输出一个递增序列的第k个排列。
思路
对于n个数的排列,只有当后面n-1个数完成一次全排列后,才会对第一个数进行更替,即更换周期cycle为(n-1)!,且第一个数字是按照n个数中从最小数到最大数依次更替的。令 \(count=\frac{k-1}{cycle}+1\),则排在第一位的数字是这n个数中的第count大,将该数字加入结果序列。每次得到第一位数字后,缩小n的规模求剩下n-1个数中应当排在第一位的数,同时要更新n-1个数中要求的第k个排列 \(k=(k-1)\ \%\ cycle+1\)。
笨办法:也可以结合 0031. Next Permutation,从递增序列开始依次计算下一个排列。
代码实现
Java
公式计算
class Solution {
public String getPermutation(int n, int k) {
StringBuilder ans = new StringBuilder();
boolean[] used = new boolean[n]; //标记剩下的数字
while (n >= 1) {
// 注意n=1时实际周期为0,将其周期设为1便于合并处理
int cycle = n == 1 ? 1 : factorial(n - 1);
int count = (k - 1) / cycle + 1;
// 找到剩余数字中的第count大,并将其加入结果序列
for (int i = 0; i < used.length; i++) {
if (!used[i]) {
if (count == 1) {
ans.append(i + 1);
used[i] = true;
break;
}
count--;
}
}
// 缩小待排列序列的规模
n--;
k = (k - 1) % cycle + 1;
}
return ans.toString();
}
private int factorial(int n) {
int ans = 1;
while (n != 1) {
ans *= n--;
}
return ans;
}
}
nextPermutation
class Solution {
public String getPermutation(int n, int k) {
char[] a = new char[n];
for (int i = 0; i < n; i++) {
a[i] = (char) (i + 1 + '0');
}
int count = 1;
while (count < k) {
nextPermutation(a);
count++;
}
return String.valueOf(a);
}
// 依照当前排列计算下一个排列
private void nextPermutation(char[] a) {
int i = a.length - 2;
while (i >= 0 && a[i] >= a[i + 1]) {
i--;
}
// i==-1说明整个数组是逆序排列,已经是字典序中的最后一个排列
if (i == -1) {
return;
}
int j = a.length - 1;
while (a[j] <= a[i]) {
j--;
}
swap(a, i, j);
reverse(a, i + 1, a.length - 1);
}
private void reverse(char[] a, int left, int right) {
while (left < right) {
swap(a, left++, right--);
}
}
private void swap(char[] a, int i, int j) {
char temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
JavaScript
公式计算
/**
* @param {number} n
* @param {number} k
* @return {string}
*/
var getPermutation = function (n, k) {
let used = new Array(n)
let ans = ''
while (n > 0) {
let cycle = n === 1 ? 1 : factorial(n - 1)
let rank = Math.trunc((k - 1) / cycle) + 1
let count = 0
for (let i = 0; i < used.length; i++) {
if (!used[i]) {
count++
if (count === rank) {
ans += i + 1
used[i] = true
break
}
}
}
k = ((k - 1) % cycle) + 1
n--
}
return ans
}
let factorial = function (n) {
return n === 1 ? n : n * factorial(n - 1)
}
nextPermutation
/**
* @param {number} n
* @param {number} k
* @return {string}
*/
var getPermutation = function (n, k) {
let count = 1
let arr = new Array(n).fill(0).map((v, index) => index + 1)
while (count !== k) {
nextPermutation(arr)
count++
}
return arr.join('')
}
let nextPermutation = function (arr) {
let i = arr.length - 2
while (i >= 0 && arr[i] >= arr[i + 1]) {
i--
}
if (i === -1) {
return
}
let j = arr.length - 1
while (arr[i] >= arr[j]) {
j--
}
[arr[i], arr[j]] = [arr[j], arr[i]]
let left = i + 1,
right = arr.length - 1
while (left < right) {
[arr[left++], arr[right--]] = [arr[right], arr[left]]
}
}