问题描述:有n个不同的数,现在要从中选取m个,需要按升序输出所有排列,比如从0到9中选出2个的所有排列为
0 1
0 2
0 3
...
0 9
1 0
1 2
...
9 8
一共10*9=90个
c++代码为
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
//获取下一个n选m的排列
struct Permutation {
const int m; //排列的位数
set<int> used_values; //记录目前已经使用了哪些数字
set<int> unused_values; //哪些数字当前未被使用
vector<int> answer; //目前的答案,是一个长度为m的升序序列
/*
输入时保证values为升序序列且无重复元素
*/
Permutation(const vector<int>& values,int m) :
m(m)
, answer(m)
{
//首先把values的所有元素都复制到未使用的列表中
for (auto e : values)
set_unused(e);
//把s中的前m个元素复制到answer中,作为初始解
for (int i = 0; i < m; ++i) {
int x = values[i];
answer[i] = values[i];
set_used(x);
}
}
//在未使用的数字中找出第一个>value的值,
//如果没有则返回-1
int next_unused(int value)const {
if (*(unused_values.rbegin()) <= value) {
return -1;
}
else {
for (int e : unused_values) {
if (e > value)
return e;
}
}
return -1;
}
void set_used(int value) {
used_values.insert(value);
unused_values.erase(value);
}
void set_unused(int value) {
used_values.erase(value);
unused_values.insert(value);
}
bool next() //输出下一个组合,如果已经到头,则返回false
{
for (int i = m-1; i >=0; --i) {
/*定位到第一个可以增加的位置,然后增加该位的值
条件为,本位的值小于等于最大值,且当前值到最大值之间至少有一个还未使用的
*/
int v = answer[i];
int x = next_unused(v);
if (x < 0) {
set_unused(v); //本位没有增加的可能,则把本位的值设为未使用,以便将来重新填充
}
else {
answer[i] = x; //更新本位的值
set_unused(v);
set_used(x);
fill(i); //把本位右边的空位用未使用过的数字进行填充,得到一个新的升序子序列
return true; //已经得到下一组解,停止循环,直接返回true
}
}
return false;
}
//把index右边的所有空位用未使用过的元素进行填充,得到一个升序子序列
void fill(int index) {
auto it = unused_values.begin();
for(int i=index+1;i<m;++i,++it)
answer[i] = *it;
for (int i = index+1; i < m; ++i)
set_used(answer[i]); //记录使用过的数字
}
void print()const {
for (auto e : answer)
cout << e << " ";
cout << endl;
}
};
int main() {
vector<int> values(10);
for (int i = 0; i < 10; ++i)
values[i] = i;
Permutation c1(values, 4);
do {
c1.print();
} while (c1.next());
return 0;
}
0 1 2 3
0 1 2 4
0 1 2 5
0 1 2 6
0 1 2 7
0 1 2 8
0 1 2 9
0 1 3 2
0 1 3 4
0 1 3 5
....
9 8 7 5
9 8 7 6
这是10个选4个的所有可能排列,符合预期