交换法
交换法的优点:不需要额外的标记数组,空间复杂度更低,代码更简洁。需要注意的是,这个方式生成的全排列并非是字典序。
#include <iostream>
#include <algorithm>
using namespace std;
int arr[10] = {0, 1, 2, 3, 4};
void dfs(int x) {
if (x > 4) {
for (int i = 1; i <= 4; i++) {
cout << arr[i] << " ";
}
cout << endl;
return;
}
for (int i = x; i <= 4; i++) {
swap(arr[x], arr[i]); // 交换当前位置和后续位置
dfs(x + 1); // 递归处理下一个位置
swap(arr[x], arr[i]); // 回溯,交换回来
}
}
int main() {
dfs(1);
return 0;
}标记数组法
#include <iostream>
#include <algorithm>
using namespace std;
int res[10],arr[10] = {0, 1, 2, 3, 4};
bool used[10];
void dfs(int x){
if(x>4){
for(int i=1;i<=4;i++)cout << res[i] << " ";
cout << endl;
return;
}
for(int i=1;i<=4;i++){
if(!used[i]){
used[i]=true;
res[x]=arr[i];
dfs(x+1);
used[i]=false;
}
}
}
int main(){
dfs(1);
}结果对比
| 序号 | 交换法生成顺序 | 标记法生成顺序(字典序) |
|---|---|---|
| 1 | 1 2 3 4 | 1 2 3 4 |
| 2 | 1 2 4 3 | 1 2 4 3 |
| 3 | 1 3 2 4 | 1 3 2 4 |
| 4 | 1 3 4 2 | 1 3 4 2 |
| 5 | 1 4 3 2 | 1 4 2 3 |
| 6 | 1 4 2 3 | 1 4 3 2 |
| 7 | 2 1 3 4 | 2 1 3 4 |
| 8 | 2 1 4 3 | 2 1 4 3 |
| 9 | 2 3 1 4 | 2 3 1 4 |
| 10 | 2 3 4 1 | 2 3 4 1 |
| 11 | 2 4 3 1 | 2 4 1 3 |
| 12 | 2 4 1 3 | 2 4 3 1 |
| 13 | 3 2 1 4 | 3 1 2 4 |
| 14 | 3 2 4 1 | 3 1 4 2 |
| 15 | 3 1 2 4 | 3 2 1 4 |
| 16 | 3 1 4 2 | 3 2 4 1 |
| 17 | 3 4 1 2 | 3 4 1 2 |
| 18 | 3 4 2 1 | 3 4 2 1 |
| 19 | 4 2 3 1 | 4 1 2 3 |
| 20 | 4 2 1 3 | 4 1 3 2 |
| 21 | 4 3 2 1 | 4 2 1 3 |
| 22 | 4 3 1 2 | 4 2 3 1 |
| 23 | 4 1 3 2 | 4 3 1 2 |
| 24 | 4 1 2 3 | 4 3 2 1 |
全排列的STL标准库实现
std::next_permutation 是 C++ 标准库
函数原型
template< class BidirIt >
bool next_permutation( BidirIt first, BidirIt last );
// 也可以自定义比较规则
template< class BidirIt, class Compare >
bool next_permutation( BidirIt first, BidirIt last, Compare comp );参数说明
first, last: 要生成排列的序列的起始和末尾迭代器(通常用 begin(), end())。
comp: 自定义的比较函数对象,用于定义“小于”的概念。
返回值
true: 如果成功生成下一个更大的排列。
false: 如果当前序列已经是最大的排列(即降序排列)。此时,函数会将序列重置为最小的排列(即升序排列)。
核心特性
会修改原序列:该函数是原地操作的,会直接改变传入的容器。
需要序列有序:为了生成所有排列,通常需要先对序列进行升序排序。
重复元素:函数能够正确处理序列中的重复元素,不会生成重复的排列。
应用于vector:
#include <iostream>
#include <vector>
#include <algorithm> // 包含 next_permutation
using namespace std;
int main() {
vector<int> vec = {1, 2, 3};
// 重要:先排序以确保从最小排列开始
// sort(vec.begin(), vec.end());
// 本例中 {1,2,3} 已经是最小排列,所以可省略排序
cout << "All permutations of {1, 2, 3}:\n";
do {
for (int num : vec) {
cout << num << " ";
}
cout << endl;
} while (next_permutation(vec.begin(), vec.end()));
// 当 next_permutation 返回 false 时循环结束
return 0;
}应用于字符串:
#include <iostream>
#include <algorithm> // 包含 next_permutation
#include <string>
using namespace std;
int main() {
string str = "abc";
// 同样,如果需要所有排列,应先排序
// sort(str.begin(), str.end());
cout << "All permutations of \"abc\":\n";
do {
cout << str << endl;
} while (next_permutation(str.begin(), str.end()));
return 0;
}处理有重复元素的序列
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> vec = {1, 1, 2};
// 必须先排序!
sort(vec.begin(), vec.end());
cout << "All permutations of {1, 1, 2}:\n";
do {
for (int num : vec) {
cout << num << " ";
}
cout << endl;
} while (next_permutation(vec.begin(), vec.end()));
return 0;
}遍历所有排列:
#include <algorithm>
#include <vector>
std::vector<int> data = {3, 1, 4, 1, 5}; // 未排序的序列
// 1. 首先必须排序!
std::sort(data.begin(), data.end());
// 2. 使用 do-while 循环
do {
// 处理当前的排列,例如打印它
// for (auto x : data) { ... }
} while (std::next_permutation(data.begin(), data.end()));

