您的位置:首页
打印全排列和stl::next_permutation
2017-07-11 09:34
316 查看
打印全排列是个有点挑战的编程问题。STL提供了stl::next_permutation完美的攻克了这个问题。
可是,假设不看stl::next_permutation,尝试自己解决,怎么做?
非常自然地,使用递归的办法:
1. 单个元素的排列仅仅有1个。
2. 多个元素的排列能够转化为:
以每一个元素为排列的首个元素,加上其它元素的排列。
有了思路,就能够编码了。
第一个版本号:
void printAllPermutations(const std::string&
prefix, int set[], int n)
[align=left]{[/align]
[align=left] using namespace std;[/align]
[align=left] char buffer[12];[/align]
for (int i=0;
i<n; ++i)
[align=left] {[/align]
[align=left] string tmp_prefix(prefix);[/align]
int ei
= set[i];
[align=left] _itoa_s(ei, buffer, 12, 10);[/align]
[align=left] tmp_prefix += buffer;[/align]
if (n
== 1)
[align=left] {[/align]
[align=left] cout << tmp_prefix.c_str() << endl;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
tmp_prefix += "
" ;
//
shift set[0,i) to right by 1
for (int j=i-1;
j>=0; --j)
[align=left] {[/align]
[align=left] set[j+1] = set[j];[/align]
[align=left] }[/align]
[align=left] printAllPermutations(tmp_prefix, set+1, n-1);[/align]
//
shift set[0,i) to left by 1
for (int j=0;
j<i; ++j)
[align=left] {[/align]
[align=left] set[j] = set[j+1];[/align]
[align=left] }[/align]
[align=left] set[i] = ei;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]}[/align]
測试:
[align=left]int myints[] = {1,2,3,4};[/align]
printAllPermutations( "" ,
myints, 4);
通过。
这样的方法的缺点是产生了大量的string对象。
怎么避免呢?
第二个版本号:
[align=left][/align]
[align=left]void printAllPermutations2(int set[], int n, int from)[/align]
[align=left]{[/align]
[align=left] using namespace std;[/align]
for (int i=from;
i<n; ++i)
[align=left] {[/align]
int ei
= set[i];
if (from
== n-1)
[align=left] {[/align]
//
it is possible use callback here instead of printing a permutation
for (int j=0;
j<n; ++j)
[align=left] {[/align]
cout << set[j] << '
' ;
[align=left] }[/align]
[align=left] cout << endl;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
//
shift set[from,i) to right by 1
for (int j=i-1;
j>=from; --j)
[align=left] {[/align]
[align=left] set[j+1] = set[j];[/align]
[align=left] }[/align]
[align=left] set[from] = ei;[/align]
[align=left] printAllPermutations2(set, n, from+1);[/align]
//
shift set[from,i) to left by 1
for (int j=from;
j<i; ++j)
[align=left] {[/align]
[align=left] set[j] = set[j+1];[/align]
[align=left] }[/align]
[align=left] set[i] = ei;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left][/align]
測试:
[align=left]int myints[] = {1,2,3,4};[/align]
[align=left]printAllPermutations2(myints, 4, 0);[/align]
通过。
第二个版本号相比第一个版本号的还有一个改进是能够非常easy地改变成回调函数的形式,扩展函数的用途。而不不过打印排列。
似乎非常不错了。
可是和stl::next_permutation相比,以上的方案就太逊了。
1. stl::next_permutation支持部分排列,而不必是全排列。你能够从不论什么一个排列開始,能够随时退出next_permutation循环。
2. stl::next_permutation支持多重集的排列。比如:
int myints[]
= {1,2,2,2};
[align=left]do {[/align]
std::cout << myints[0] << '
' << myints[1] << ' ' <<
myints[2] << ' ' << myints[3]
<< '\n';
} while (
std::next_permutation(myints,myints+4) );
输出:
1 2 2 2
2 1 2 2
2 2 1 2
2 2 2 1
没有反复的排列。
stl::next_permutation这么强大,非常值得看看它到底是怎么实现的。
[align=left]// TEMPLATE FUNCTION next_permutation[/align]
[align=left]template < class _BidIt> inline[/align]
bool _Next_permutation(_BidIt
_First, _BidIt _Last)
{ // permute
and test for pure ascending, using operator<
[align=left] _DEBUG_RANGE(_First, _Last);[/align]
[align=left] _BidIt _Next = _Last;[/align]
if (_First
== _Last || _First == --_Next)
[align=left] return (false );[/align]
for (;
; )
{ //
find rightmost element smaller than successor
[align=left] _BidIt _Next1 = _Next;[/align]
if (_DEBUG_LT(*--_Next,
*_Next1))
{ //
swap with rightmost element that's smaller, flip suffix
[align=left] _BidIt _Mid = _Last;[/align]
for (;
!_DEBUG_LT(*_Next, *--_Mid); )
[align=left] ;[/align]
[align=left] std::iter_swap(_Next, _Mid);[/align]
[align=left] std::reverse(_Next1, _Last);[/align]
[align=left] return (true );[/align]
[align=left] }[/align]
if (_Next
== _First)
{ //
pure descending, flip all
[align=left] std::reverse(_First, _Last);[/align]
[align=left] return (false );[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]template < class _BidIt> inline[/align]
bool next_permutation(_BidIt
_First, _BidIt _Last)
{ // permute
and test for pure ascending, using operator<
return _Next_permutation(_CHECKED_BASE(_First),
_CHECKED_BASE(_Last));
[align=left] }[/align]
代码不长,但须要研究才干理解。
非常多算法都是这种。
这个算法能够概括为:
假设仅仅有零个或一个元素,返回false[b],表示回到全排列的起点。
[/b]
否则。从右边開始。找到第一个不是递减的元素,即E(i) < E(i+1),从E(i+1)一直到E(n)都是不增的。
假设找到。从右边開始。找到大于E(i)的那个元素E(x)【一定会找到】,交换E(i)和E(x),然后把E[i+1, n]范围内的元素反转。
返回true。
假设找不到,把整个范围内的元素反转,返回false,表示回到全排列的起点。
为什么这个算法可行呢?看以下1 2 3 4的全排列。
能够非常easy地看到,
假设把每一个排列看成一个数,那么下一个排列大于上一个排列。
由上可知,第一个排列是最小排列【不减排列】。最后一个排列是最大排列【不增排列】。
最小排列和最大排列是反序的关系。
算法的关键:从E(i+1)一直到E(n)都是不增的。
这个特性说明,这一范围的元素的排列是一个最大排列,下一个排列必然是找到这一范围内大于这一范围的前一元素的元素,交换这两个元素,交换后E[i+1, n]仍为不增排列【最大排列】。反转之后,变成最小排列。这样处理后得到的排列正好是E[0,n]的下一个排列。
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
可是,假设不看stl::next_permutation,尝试自己解决,怎么做?
非常自然地,使用递归的办法:
1. 单个元素的排列仅仅有1个。
2. 多个元素的排列能够转化为:
以每一个元素为排列的首个元素,加上其它元素的排列。
有了思路,就能够编码了。
第一个版本号:
void printAllPermutations(const std::string&
prefix, int set[], int n)
[align=left]{[/align]
[align=left] using namespace std;[/align]
[align=left] char buffer[12];[/align]
for (int i=0;
i<n; ++i)
[align=left] {[/align]
[align=left] string tmp_prefix(prefix);[/align]
int ei
= set[i];
[align=left] _itoa_s(ei, buffer, 12, 10);[/align]
[align=left] tmp_prefix += buffer;[/align]
if (n
== 1)
[align=left] {[/align]
[align=left] cout << tmp_prefix.c_str() << endl;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
tmp_prefix += "
" ;
//
shift set[0,i) to right by 1
for (int j=i-1;
j>=0; --j)
[align=left] {[/align]
[align=left] set[j+1] = set[j];[/align]
[align=left] }[/align]
[align=left] printAllPermutations(tmp_prefix, set+1, n-1);[/align]
//
shift set[0,i) to left by 1
for (int j=0;
j<i; ++j)
[align=left] {[/align]
[align=left] set[j] = set[j+1];[/align]
[align=left] }[/align]
[align=left] set[i] = ei;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]}[/align]
測试:
[align=left]int myints[] = {1,2,3,4};[/align]
printAllPermutations( "" ,
myints, 4);
通过。
这样的方法的缺点是产生了大量的string对象。
怎么避免呢?
第二个版本号:
[align=left][/align]
[align=left]void printAllPermutations2(int set[], int n, int from)[/align]
[align=left]{[/align]
[align=left] using namespace std;[/align]
for (int i=from;
i<n; ++i)
[align=left] {[/align]
int ei
= set[i];
if (from
== n-1)
[align=left] {[/align]
//
it is possible use callback here instead of printing a permutation
for (int j=0;
j<n; ++j)
[align=left] {[/align]
cout << set[j] << '
' ;
[align=left] }[/align]
[align=left] cout << endl;[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
//
shift set[from,i) to right by 1
for (int j=i-1;
j>=from; --j)
[align=left] {[/align]
[align=left] set[j+1] = set[j];[/align]
[align=left] }[/align]
[align=left] set[from] = ei;[/align]
[align=left] printAllPermutations2(set, n, from+1);[/align]
//
shift set[from,i) to left by 1
for (int j=from;
j<i; ++j)
[align=left] {[/align]
[align=left] set[j] = set[j+1];[/align]
[align=left] }[/align]
[align=left] set[i] = ei;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]}[/align]
[align=left][/align]
測试:
[align=left]int myints[] = {1,2,3,4};[/align]
[align=left]printAllPermutations2(myints, 4, 0);[/align]
通过。
第二个版本号相比第一个版本号的还有一个改进是能够非常easy地改变成回调函数的形式,扩展函数的用途。而不不过打印排列。
似乎非常不错了。
可是和stl::next_permutation相比,以上的方案就太逊了。
1. stl::next_permutation支持部分排列,而不必是全排列。你能够从不论什么一个排列開始,能够随时退出next_permutation循环。
2. stl::next_permutation支持多重集的排列。比如:
int myints[]
= {1,2,2,2};
[align=left]do {[/align]
std::cout << myints[0] << '
' << myints[1] << ' ' <<
myints[2] << ' ' << myints[3]
<< '\n';
} while (
std::next_permutation(myints,myints+4) );
输出:
1 2 2 2
2 1 2 2
2 2 1 2
2 2 2 1
没有反复的排列。
stl::next_permutation这么强大,非常值得看看它到底是怎么实现的。
[align=left]// TEMPLATE FUNCTION next_permutation[/align]
[align=left]template < class _BidIt> inline[/align]
bool _Next_permutation(_BidIt
_First, _BidIt _Last)
{ // permute
and test for pure ascending, using operator<
[align=left] _DEBUG_RANGE(_First, _Last);[/align]
[align=left] _BidIt _Next = _Last;[/align]
if (_First
== _Last || _First == --_Next)
[align=left] return (false );[/align]
for (;
; )
{ //
find rightmost element smaller than successor
[align=left] _BidIt _Next1 = _Next;[/align]
if (_DEBUG_LT(*--_Next,
*_Next1))
{ //
swap with rightmost element that's smaller, flip suffix
[align=left] _BidIt _Mid = _Last;[/align]
for (;
!_DEBUG_LT(*_Next, *--_Mid); )
[align=left] ;[/align]
[align=left] std::iter_swap(_Next, _Mid);[/align]
[align=left] std::reverse(_Next1, _Last);[/align]
[align=left] return (true );[/align]
[align=left] }[/align]
if (_Next
== _First)
{ //
pure descending, flip all
[align=left] std::reverse(_First, _Last);[/align]
[align=left] return (false );[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]template < class _BidIt> inline[/align]
bool next_permutation(_BidIt
_First, _BidIt _Last)
{ // permute
and test for pure ascending, using operator<
return _Next_permutation(_CHECKED_BASE(_First),
_CHECKED_BASE(_Last));
[align=left] }[/align]
代码不长,但须要研究才干理解。
非常多算法都是这种。
这个算法能够概括为:
假设仅仅有零个或一个元素,返回false[b],表示回到全排列的起点。
[/b]
否则。从右边開始。找到第一个不是递减的元素,即E(i) < E(i+1),从E(i+1)一直到E(n)都是不增的。
假设找到。从右边開始。找到大于E(i)的那个元素E(x)【一定会找到】,交换E(i)和E(x),然后把E[i+1, n]范围内的元素反转。
返回true。
假设找不到,把整个范围内的元素反转,返回false,表示回到全排列的起点。
为什么这个算法可行呢?看以下1 2 3 4的全排列。
能够非常easy地看到,
假设把每一个排列看成一个数,那么下一个排列大于上一个排列。
由上可知,第一个排列是最小排列【不减排列】。最后一个排列是最大排列【不增排列】。
最小排列和最大排列是反序的关系。
算法的关键:从E(i+1)一直到E(n)都是不增的。
这个特性说明,这一范围的元素的排列是一个最大排列,下一个排列必然是找到这一范围内大于这一范围的前一元素的元素,交换这两个元素,交换后E[i+1, n]仍为不增排列【最大排列】。反转之后,变成最小排列。这样处理后得到的排列正好是E[0,n]的下一个排列。
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
相关文章推荐
- 打印全排列和stl::next_permutation
- 【STL】全排列生成算法:next_permutation
- 计算下一个全排列(以STL中的next_permutation为例)
- STL 中 next_permutation 的全排列思路
- STL实现全排列 next_permutation
- 计算下一个全排列(以STL中的next_permutation为例)
- [STL]next_permutation全排列
- STL之全排列next_permutation()函数的用法
- stl全排列 qsort+next_permutation(a,a+n) -->全排列
- 枚举全排列 STL next_permutation
- STL: <algorithm>中 next_permutation()生成全排列
- 7.2.1 生成1~n的排列(全排列)【STL__next_permutation()_的应用】
- STL next_permutation全排列 assign 复制
- 用STL的next_permutation算法实现全排列
- STL实现全排列 next_permutation
- STL 排序算法之全排列sort和next_permutation
- STL next_permutation(a,a+n) 生成一个序列的全排列。满足可重集。
- stl全排列 qsort+next_permutation(a,a+n) -->全排列 .
- stl库之next_permutation实现全排列(二)
- 【C++_STL_next_permutation】全排列讲解