c++输出n选m的所有排列

19 篇文章 1 订阅

问题描述:有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个的所有可能排列,符合预期

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值