最近在牛客和领扣上刷题,碰到了一些全排列的问题,总结一下。
我们首先来看一个问题?
题目来源:字符串的排列
题目描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
解题思路:使用递归的方式来实现,首先固定第一个字符,求剩余字符的排列;求剩余字符排列时跟原问题一样。
- 遍历出所有可能出现在第一个位置的字符(依次将第一个字符和后面所有的字符交换);
- 固定第一个字符,求后面字符的全排列(在将第一个字符同后面字符交换的过程中,进行递归)。
- 本题中结束条件为剩余字符数为1的时候。
下面我们来看代码实现:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// 字符全排列
void permutation(std::string& str,
std::vector<std::string>& result, int begin) {
if (begin == str.size() - 1) {
if (find(result.begin(), result.end(), str) == result.end()) {
result.push_back(str);
}
} else {
for (int i = begin; i < (int)str.size(); ++i) {
std::swap(str[i], str[begin]);
permutation(str, result, begin + 1);
std::swap(str[i], str[begin]);
}
}
}
int main() {
std::string str = "ABC";
std::vector<std::string> result;
permutation(str, result, 0);
for (const auto& e : result) {
std::cout << e << std::endl;
}
return 0;
}
运行结果如下:
下面我们来看一下代码的执行过程:
同理,我们也可以打印数字的全排列,代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
// 数字全排列
void permutation(std::vector<int>& arr,
std::vector<std::vector<int>>& result, int begin) {
if (begin == arr.size() - 1) {
result.push_back(arr);
}
for (int i = begin; i < (int)arr.size(); ++i) {
std::swap(arr[begin], arr[i]);
permutation(arr, result, begin + 1);
std::swap(arr[begin], arr[i]);
}
}
int main() {
std::vector<int> arr = {
1, 2, 3, 4
};
std::vector<std::vector<int>> result;
permutation(arr, result, 0);
for (int i = 0; i < (int)result.size(); ++i) {
for (int j = 0; j < (int)result[i].size(); ++j) {
std::cout << result[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
结果如下:
全排列除了可以使用递归自己实现外,还可以使用C++标准库中的prev_permutation和next_permutation函数来实现。
prev_permutation和next_permutation的区别:
- 从字面上来理解,prev_permutation为前一个排列组合,next_permutation为后一个排列组合。
- 我们举个例子来理解什么是前一个排列组合,什么是后一个排列组合。{A,B,C}三个字符有六种排列组合方式:ABC、ACB、BAC、BCA、CAB、CBA;这些排列组合按照字典序排列,ABC为第一个排列组合,它的后一个排列组合为ACB,它没有前一个排列组合;同样的CBA是最后一个排列组合,它的前一个排列组合为CAB,它没有后一个排列组合。
- prev_permutation会取得[first, last)所标识序列的前一个排列组合,如果没有前一个排列组合,则返回false,否则返回true。
- next_permutation会取得[frist, last)所标识序列的后一个排列组合,如果没有后一个排列组合,则返回false,否则返回true。
- 我们可以通过传入仿函数cmp来控制排序方式。
库函数使用代码示例:
使用next_permutation实现数字全排列:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> arr = {
1, 2, 3, 4
};
// 需要先排序
std::sort(arr.begin(), arr.end());
std::vector<std::vector<int>> result;
result.push_back(arr);
while (std::next_permutation(arr.begin(), arr.end())) {
result.push_back(arr);
}
for (int i = 0; i < (int)result.size(); ++i) {
for (int j = 0; j < (int)result[i].size(); ++j) {
std::cout << result[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
运行结果如下:
使用prev_permutation实现字符串的全排列:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
std::string str = "ABC";
std::vector<std::string> result;
// 先排序
std::sort(str.begin(), str.end(), std::greater<char>());
result.push_back(str);
while (std::prev_permutation(str.begin(), str.end())) {
result.push_back(str);
}
for (const auto& e : result) {
std::cout << e << std::endl;
}
return 0;
}
运行结果如下: