一、题目
给出集合 [1,2,3,…,n]
,其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
二、思路
1)回溯算法求出第k个排列,性能不佳,lettcode中跑用例超时。
2)
以n = 4, k = 14的情况来举例。
{1, 2, 3, 4}. 4 * 3 * 2 * 1 = 24
一共有24中组合方式,其中
1 + {2, 3, 4} 3 * 2 * 1 = 6
2 + {1, 3, 4} 3 * 2 * 1 = 6
3 + {1, 2, 4} 3 * 2 * 1 = 6
4 + {1, 2, 3} 3 * 2 * 1 = 6
当我们寻找第14个排列时,势必可以确定第一个数字是3,然后我们在{1, 2, 4} 中寻找
1 + {2, 4} 2 * 1 = 2
2 + {1, 4} 2 * 1 = 2
4 + {1, 2} 2 * 1 = 2
由1开头的排列有2个,由2开头的排列、由4开头的排列也均有2个。当我们寻找第2个排列时,势必可以确定第二个数字是1。然后我们将在{2, 4}中寻找第2个排列。
2 + {4} 1
4 + {2} 1
由2开头的排列有1个,由4开头的排列也有1个。当我们寻找第2个排列时,势必可以确定第三个数字是4。然后我们将在{2}中寻找第1个排列。由于{2}中只有一个数字,其第一个排列,即第四个数字必定是2。
我们就可以求出n = 4, k = 14的情况的结果是3142。
三、实现
1)
public static String getPermutation(int n, int k) {
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = i + 1;
}
List<Integer> list = new ArrayList<>();
List<List<Integer>> lists = new ArrayList<>();
back(nums, list, lists);
list = lists.get(k);
StringBuffer sb = new StringBuffer();
for (int str : list) {
sb.append(str);
}
return sb.toString();
}
public static void back(int[] nums, List<Integer> list, List<List<Integer>> lists) {
if (list.size() == nums.length) {
lists.add(new ArrayList<>(list));
return;
}
for (int num : nums) {
if (!list.contains(num)) {
list.add(num);
back(nums, list, lists);
list.remove(list.size() - 1);
}
}
}
2)
public static String getPermutation(int n, int k) {
List<Integer> list = new ArrayList<>();
k = k - 1;
StringBuilder sb = new StringBuilder();
int times = n - 1;
for (int i = 1; i <= n; i++) {
list.add(i);
}
int factorail = 1; //阶乘
for (int i = 2; i < n; i++) {
factorail *= i;
}
while (times >= 0) {
int indexList = k / factorail;
sb.append(list.get(indexList));
list.remove(indexList);
k = k % factorail;
if (times != 0) {
factorail /= times;
}
times--;
}
return sb.toString();
}