利用LeetCode46题全排列来验证一下是否正确
这个方法把n!个排列与0n!-1之间的数一一对应起来,这样,我们就可以按照0n!-1的次序,逐一生成相关的排列。这个对应的关键在于0~n!-1之间的数m,可以用如下的方式表示: m=an−1⋅(n−1)!+an−2⋅(n−2)!+…+a1⋅1!,其中0≤ai≤i0≤ai≤i,
故m对应序列(an−1,an−2,…a1),现在再把这个序列和排列一一对应起来,n-1位的序列对应n位的排列。
aiai 表示排列p中的数i+1所在位置的右边比它小的数的个数。比如对于排列p=4213,4,3,2的逆序数分别为3,0,1,由此对应为(a2,a1,a0)(a2,a1,a0),反过来呢,如果知道了逆序数(a2,a1,a0)==(3,0,1),也可以一一恢复出排列来。比4小的有3个,4只能放最左边,比3小的为0个,3只能放最右边,比2小的一个,2放第二个位置,剩下的放1,故为4213。
N | a3a2a1 | p1p2p3p4 | N | a3a2a1 | p1p2p3p4 |
---|---|---|---|---|---|
0 | 000 | 1234 | 12 | 200 | 1423 |
1 | 001 | 2134 | 13 | 201 | 2413 |
2 | 010 | 1324 | 14 | 210 | 1432 |
3 | 011 | 2314 | 15 | 211 | 2431 |
4 | 020 | 3124 | 16 | 220 | 3412 |
5 | 021 | 3214 | 17 | 221 | 3421 |
6 | 100 | 1243 | 18 | 300 | 4123 |
7 | 101 | 2143 | 19 | 301 | 4213 |
8 | 110 | 1342 | 20 | 310 | 4132 |
9 | 111 | 2341 | 21 | 311 | 4231 |
10 | 120 | 3142 | 22 | 320 | 4312 |
11 | 121 | 3241 | 23 | 321 | 4321 |
class Solution {
public:
int setPosition(vector& permutation, int count, int num, int flag, int zeroposition){
int zeros = 0;
if(flag == 1){
for(int i = permutation.size()-1;i>=0;i--){
if(permutation[i]==0&&i!=zeroposition) zeros++;
if(zeros == count +1){
permutation[i] = num;
if(num == 0) return i;
else return -1;
}
}
}
for(int i = permutation.size()-1;i>=0;i--){
if(permutation[i]==0) zeros++;
if(zeros == count +1){
permutation[i] = num;
if(num == 0) return i;
else return -1;
}
}
return -1;
}
vector> permute(vector& nums) {
int n = nums.size();
vector> ans;
if(n<=0) return ans;
vector facorials(n+1);
facorials[0] = 1;
for(int i = 1;i<=n;i++) facorials[i] = i * facorials[i-1];
sort(nums.begin(),nums.end());
int position, wuyong;
for(int m = 0;m permutation(n);
int flag = 0;
for(int i = n-1;i>=0;i--){
int count = remainder / facorials[i];
remainder = remainder % facorials[i];
if(nums[i] == 0){ // 判断是否是0的原因是如果不判断会把放好的0当做没放东西,所以要根据是否放了0来做出一点小小的改变
flag = 1;
position = setPosition(permutation, count, nums[i], 0, -1);
continue;
}
if(flag == 1) wuyong = setPosition(permutation, count, nums[i], flag, position);
else wuyong = setPosition(permutation, count, nums[i], 0, -1);
}
ans.push_back(permutation);
}
return ans;
}
};
字典序生成法在当前已经生成的排列上寻找下一个字典序的排列:
比如排列123654,首先找到后缀654,在找到中间最小大于3的4,交换得到124653,然后将后缀逆序得到124356,又如排列123645,找到后缀5,交换得到123654,后缀只有一个,逆序后不变仍为123654。
class Solution {
public:
int jieche(int n){
if(n==1) return 1;
return n*jieche(n-1);
}
vector> permute(vector& nums) {
int n = nums.size();
vector> ans;
if(n<=0) return ans;
sort(nums.begin(),nums.end());
ans.push_back(nums);
int cishu = jieche(n);
for(int p = 1;p<=cishu;p++){
int x = ans.size();
vector last = ans[x-1];
int i = n - 1;
while(i > 0){
if(last[i] > last[i-1]) break;
i--;
}
if(i==0) break;
int j = n - 1;
while(j>=i){
if(last[j]>last[i-1]) break;
j--;
}
swap(last[i-1],last[j]);
int k = n-1;
while(i
利用LeetCode测试[1,2,3,4]结果如下
[[1,2,3,4],[1,2,4,3],[1,3,2,4],[1,3,4,2],[1,4,2,3],[1,4,3,2],[2,1,3,4],[2,1,4,3],[2,3,1,4],[2,3,4,1],[2,4,1,3],[2,4,3,1],[3,1,2,4],[3,1,4,2],[3,2,1,4],[3,2,4,1],[3,4,1,2],[3,4,2,1],[4,1,2,3],[4,1,3,2],[4,2,1,3],[4,2,3,1],[4,3,1,2],[4,3,2,1]]
[[1,2,3,4],[1,2,4,3],[1,3,2,4],[1,3,4,2],[1,4,3,2],[1,4,2,3],[2,1,3,4],[2,1,4,3],[2,3,1,4],[2,3,4,1],[2,4,3,1],[2,4,1,3],[3,2,1,4],[3,2,4,1],[3,1,2,4],[3,1,4,2],[3,4,1,2],[3,4,2,1],[4,2,3,1],[4,2,1,3],[4,3,2,1],[4,3,1,2],[4,1,3,2],[4,1,2,3]]
可见结果正确
这个方法比较简单,LeetCode上写的也比较多了,我就直接放一下代码就好了
class Solution {
void backtrack(vector>& ans, vector& output, int begin, int length){
if(begin == length){
ans.emplace_back(output);
return;
}
for(int i = begin; i < length; i++){
swap(output[i],output[begin]);
backtrack(ans, output, begin+1, length);
swap(output[i],output[begin]);
}
}
public:
vector> permute(vector& nums) {
vector> ans;
backtrack(ans, nums, 0, nums.size());
return ans;
}
};
p.s. 还挖了两个坑到时候填hhh