全排列的概念:
给定n个元素,输出所有不同的排列情况。
递归的思路:
例子:对于字符串 abcd
的全排列,我们可以这样看待。
它的全排列应当是以字符串abcd
中任一字符+其它字符的全排列:
就abcd
这个例子,分以下情况:
a
+ bcd
的全排列
b
+ acd
的全排列
c
+ abd
的全排列
d
+ abc
的全排列
可以看到我们已经能构造出子问题了。
实现的方式就是分别将当前字符串每个位置分别与首字符作对换,然后求除首字符位置外的部分的全排列。一直这样递归下去。
上代码:
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 46 47 48 49 50 51
| #include<iostream> #include<vector> using namespace std; static vector<vector<char>> vec; void swap(vector<char>& arr, int a, int b) { char tem = arr[a]; arr[a] = arr[b]; arr[b] = tem; } void getPermutation(vector<char>& arr, int k, int n) { int i, size; if (k == n) { size = vec.size(); vec.resize(vec.size() + 1); size = vec.size(); vec[size-1].resize(n); for (i = 0; i < n; i++) { vec[size - 1][i] = arr[i]; } return; } for (i = k; i < n; i++) { swap(arr, k, i); getPermutation(arr, k + 1, n); swap(arr, k, i); } } int main() { int n; while (cin >> n) { vector<char> arr(n, 0); for (int i = 0; i < n; i++) cin >> arr[i]; getPermutation(arr, 0, n); int count = vec.size(); int size; for (int i = 0; i < count; i++) { size = vec[i].size(); for (int k = 0; k < size; k++) { cout << vec[i][k]; } cout << endl; } } return 0; }
|
复杂度分析
时间复杂度:
O(n!)
空间复杂度:
O(n!)
函数栈的最大深度应该是 n,(除main函数)。但不幸的是实际上申请销毁函数栈的次数是n!,非常可怕的开销。所以这是一个效率低下的方法。
另一方面,使用vector来保存所有的排列情况,对于有重复元素的情况是不能做到去重的,其实可以考虑用set。set的数据结构是二叉查找树,每次插入是logn的复杂度,总的时间复杂度是O(n!logn)。我选择原地爆炸。
学习完字典序方式求全排列后会将更高效的方法写上来。有条件我会做效率对比。
以上。