题目描述
我们称 n 的全排列为 1,2,3...,1,2,3...,n 按照一定顺序组成的序列。比如 33 的所有全排列为:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
现在请你生成 n 的所有全排列。
输入格式
第一行一个整数 n。
输出格式
输出所有 n 的全排列,按照字典序从小到大输出。
字典序比较大小方法为从第一个数字开始比较,若不同则比较出来大小,否则接着比较第二位,直到比较出来大小位置。样例中就是按照字典序排序后的结果。
输入数据 1
3
输出数据 1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
数据范围与提示
1<=n<=81<=n<=8
这个全排列其实很简单,它的生成算法主要有两种:递归方法和字典序生成方法。
1. 递归方法(回溯算法)
递归方法是生成全排列的一种直观方式。它通过逐个确定每个位置上的元素来构造排列。
- 初始时,所有元素未被选中。
- 从第一个位置开始,递归地选择一个未被选中的元素放到这个位置上。
- 然后对剩下的未被选中的元素做同样的操作。
- 当所有位置都被填满后,一个全排列就生成了。
- 然后回溯,更换前面位置的元素,继续生成新的排列。
这个过程类似于树的深度优先遍历。
2. 字典序生成方法
字典序方法是另一种高效的生成全排列的方法,它不依赖递归。
- 首先,将原始序列升序排序。
- 然后不断找出当前排列的下一个排列,直到不存在更大的排列。
- 找下一个排列的步骤是:
- 从后向前查找第一个相邻升序的元素对
(i, j)
,满足A[i] < A[j]
。这一步是找到需要改变的分界点。 - 再次从后向前查找第一个大于
A[i]
的元素A[k]
。 - 交换
A[i]
和A[k]
。 - 反转
A[i]
后面的所有元素。
- 从后向前查找第一个相邻升序的元素对
例如,对于序列 [1, 2, 3]
,其全排列按字典序为 [1, 2, 3]
-> [1, 3, 2]
-> [2, 1, 3]
-> [2, 3, 1]
-> [3, 1, 2]
-> [3, 2, 1]
。
我这里用的是字典序:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; ++i) {
nums[i] = i + 1;
}
do {
for (int i = 0; i < n; ++i) {
cout << nums[i] << ' ';
}
cout << '\n';
} while (next_permutation(nums.begin(), nums.end()));
return 0;
}
代码的工作原理:
-
读取输入: 程序首先从标准输入读取一个整数
n
。 -
初始化向量: 然后,程序创建一个
std::vector<int>
,名为nums
,其大小为n
,并用1到n
的连续整数初始化。 -
生成排列:
- 使用
do-while
循环和std::next_permutation
函数,程序生成并打印出nums
向量的所有排列。 - 在每次迭代中,使用
std::next_permutation
来生成下一个字典序更大的排列。 - 当所有排列都被生成后,
next_permutation
将返回false
,循环结束。
- 使用
-
打印排列:
- 在循环的每次迭代中,打印当前的排列,每个数字后跟一个空格。
- 每个排列后打印一个换行符。