题目
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”,“132”,“213”,“231”,“312”,321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 :
输入: n = 3, k = 3
输出: “213”
注意点
1、该题解决思想是:按顺序遍历所有排列组合,然后返回需要的第k个组合(因为可以使用剪支操作,所以不需要全部遍历)。
2、遍历排列组合可参考:全排列(DFS回溯实现)
实现
public String getPermutation(int n, int k) {
//标志对应数字是否使用过,默认为false,都没使用过(排列不可重复)
boolean[] used = new boolean[n + 1];
//用于存放结果的集合
ArrayList<Integer> list = new ArrayList<>();
//计算1-n的阶层,例如factorial[2] = 2!
int[] factorial = new int[n + 1];
factorial[0] = 1;
for (int i = 1; i <= n; i ++){
factorial[i] = factorial[i - 1] * i;
}
//DFS
dfs(n, k, used, factorial, 0, list);
//将结果处理成字符串
StringBuilder result = new StringBuilder();
for (Integer i : list){
result.append(i);
}
return result.toString();
}
/**
* DFS
*
* @param n 需要求的阶层
* @param k 第k 个排列
* @param used 标志对应数字是否使用过
* @param factorial 计算1-n的阶层,例如factorial[2] = 2!
* @param index 遍历的层数(即排列的长度)
* @param path 所求的目标排列
*/
public void dfs(int n, int k, boolean[] used, int[] factorial, int index, List<Integer> path){
//如果层数等于n,即排列中已经有n个数字,退出循环
if(index == n) return;
int cnt = factorial[n - 1 - index]; //该分支产生的叶子节点数
for (int i = 1; i <= n; i ++){
//如果数字使用过,剪支,进入下一次循环
if(used[i]) continue;
//如果k > 该分支产生的叶子节点数,则k -= cnt,剪支,进入下一次循环
if(k > cnt){
k -= cnt;
continue;
}
//在结果中添加数字
path.add(i);
//将数字标志为使用过
used[i] = true;
//获取下一个数字
dfs(n, k, used, factorial, index + 1, path);
}
}