为何会有这个问题?
网易笔试很悲催的遇到这个问题,然后脑子生锈用很蠢的循环去写,而且还没写出来,这里我整理了一些其他的办法来优化,争取做到
高效,简洁,优美。
方法一
:STL的next_permutation
使用简洁,优美,但是不够高效,生成全部排列的复杂度大概为 $ O(n!*n)
$,每次生成的复杂度为O(n). next_permutation
注意,生成前需要排序,按照$ < \(的顺序排序。或者自定义comp对象。\) (fisrt,
last, comp) $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <algorithm> #include <vector> using namespace std;
void cout_nums(const vector<int> &nums){ for(int i=0;i<nums.size();i++) cout <<nums[i]<<" "; cout <<endl; }
int main(){ vector<int> nums; vector<vector<int>> permutations; for(int i=0;i<5;i++) nums.push_back(i);
sort(nums.begin(),nums.end()); permutations.push_back(vector<int>(nums.begin(),nums.end())); while(next_permutation(nums.begin(),nums.end())) permutations.push_back(vector<int>(nums.begin(), nums.end()));
for(int i=0;i<permutations.size();i++) cout_nums(permutations[i]);
return 0; }
|
方法二 DFS 递归生成
速度理论最优\(O(n!)\),但实际和方法一速度差不多,都很慢。使用更简洁,不过实现时需要注意trick。
DFS就是模仿挑选的过程,值得注意的是这里采用交换来实现的挑选。
而为了保证生成不会重复,需要在额外进行一次交换来进行复位。
证明如下: 交换,
递归调用,再交换序列会恢复原状。
对于长度为1的情况: 只有一次循环,且只和本身交换一次,显然成立
c == 第一次交换 ==> c => 递归调用(生成结果) == 第二次交换
==> c 显然符合
假设长度为n是会恢复原状,则长度n+1时
\(x, (n_1,..,n_n)\) ==
第一次交换(与元素\(n_k\)交换) ==>
\(n_x,
(n_1,n_2,...,n_{k-1},x,n_{k+1},...,n_n)\)
==> 递归调用,长度为n的情况,结束后序列显然不变 ==
第二次交换(将元素\(n_k\) 放回) ==>
得到原序列 \(x, (n_1,..,n_n)\)
数列每次打乱都会恢复原状,因此每一次挑选都不会重复,不过结果是无序的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void perm(int depth, vector<int> &nums, vector<vector<int>> &perms){ if (depth >= nums.size()){ perms.push_back(vector<int>(nums.begin(), nums.end())); return; } for (int i = depth; i < nums.size(); i++){ swap(nums[depth], nums[i]); perm(depth + 1, nums, perms); swap(nums[depth], nums[i]); } } void perm(vector<int> &nums, vector<vector<int>> &perms){ perm(0, nums, perms); }
|
使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <iostream> #include <algorithm> #include <vector> using namespace std;
void cout_nums(const vector<int> &nums){ for(int i=0;i<nums.size();i++) cout <<nums[i]<<" "; cout <<endl; }
void perm(int depth, vector<int> &nums, vector<vector<int>> &perms) { if (depth >= nums.size()) { perms.push_back(vector<int>(nums.begin(), nums.end())); return; } for (int i = depth; i < nums.size(); i++) { swap(nums[depth], nums[i]); perm(depth + 1, nums, perms); swap(nums[depth], nums[i]); } }
void perm(vector<int> &nums, vector<vector<int>> &perms) { perm(0, nums, perms); }
int main(){
vector<int> nums; vector<vector<int>> permutations; for(int i=0;i<4;i++) nums.push_back(i);
perm(nums,permutations);
for(int i=0;i<permutations.size();i++) cout_nums(permutations[i]);
return 0; }
|
结语
内置next_permutation或着采用 递归(DFS)+两次交换 实现。