打印全排列和stl::next_permutation

打印全排列是个有点挑战的编程问题。STL提供了stl::next_permutation完美的解决了这个问题。

但是,如果不看stl::next_permutation,尝试自己解决,怎么做?

很自然地,使用递归的办法:

1. 单个元素的排列只有1个。

2. 多个元素的排列可以转化为:

以每个元素为排列的首个元素,加上其他元素的排列。

有了思路,就可以编码了。

第一个版本:

void printAllPermutations(const std::string&
prefix, int set[], int n)

{

using namespace std;

char buffer[12];

for (int i=0;
i<n; ++i)

{

string tmp_prefix(prefix);

int ei
= set[i];

_itoa_s(ei, buffer, 12, 10);

tmp_prefix += buffer;

if (n
== 1)

{

cout << tmp_prefix.c_str() << endl;

}

else

{

tmp_prefix += "
" ;

//
shift set[0,i) to right by 1

for (int j=i-1;
j>=0; --j)

{

set[j+1] = set[j];

}

printAllPermutations(tmp_prefix, set+1, n-1);

//
shift set[0,i) to left by 1

for (int j=0;
j<i; ++j)

{

set[j] = set[j+1];

}

set[i] = ei;

}

}

}

测试:

int myints[] = {1,2,3,4};

printAllPermutations( "" ,
myints, 4);

通过。

这种方法的缺点是产生了大量的string对象。怎么避免呢?

第二个版本:

void printAllPermutations2(int set[], int n, int from)

{

using namespace std;

for (int i=from;
i<n; ++i)

{

int ei
= set[i];

if (from
== n-1)

{

//
it is possible use callback here instead of printing a permutation

for (int j=0;
j<n; ++j)

{

cout << set[j] << ‘
‘ ;

}

cout << endl;

}

else

{

//
shift set[from,i) to right by 1

for (int j=i-1;
j>=from; --j)

{

set[j+1] = set[j];

}

set[from] = ei;

printAllPermutations2(set, n, from+1);

//
shift set[from,i) to left by 1

for (int j=from;
j<i; ++j)

{

set[j] = set[j+1];

}

set[i] = ei;

}

}

}

测试:

int myints[] = {1,2,3,4};

printAllPermutations2(myints, 4, 0);

通过。

第二个版本相比第一个版本的另一个改进是可以很容易地改变成回调函数的形式,扩展函数的用途,而不仅仅是打印排列。

似乎很不错了。

但是和stl::next_permutation相比,以上的方案就太逊了。

1. stl::next_permutation支持部分排列,而不必是全排列。你可以从任何一个排列开始,可以随时退出next_permutation循环。

2. stl::next_permutation支持多重集的排列。例如:

int myints[]
= {1,2,2,2};

do {

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这么强大,很值得看看它究竟是怎么实现的。

// TEMPLATE FUNCTION next_permutation

template < class _BidIt> inline

bool _Next_permutation(_BidIt
_First, _BidIt _Last)

{        // permute
and test for pure ascending, using operator<

_DEBUG_RANGE(_First, _Last);

_BidIt _Next = _Last;

if (_First
== _Last || _First == --_Next)

return (false );

for (;
; )

{        //
find rightmost element smaller than successor

_BidIt _Next1 = _Next;

if (_DEBUG_LT(*--_Next,
*_Next1))

{        //
swap with rightmost element that‘s smaller, flip suffix

_BidIt _Mid = _Last;

for (;
!_DEBUG_LT(*_Next, *--_Mid); )

;

std::iter_swap(_Next, _Mid);

std::reverse(_Next1, _Last);

return (true );

}

if (_Next
== _First)

{        //
pure descending, flip all

std::reverse(_First, _Last);

return (false );

}

}

}

template < class _BidIt> inline

bool next_permutation(_BidIt
_First, _BidIt _Last)

{        // permute
and test for pure ascending, using operator<

return _Next_permutation(_CHECKED_BASE(_First),
_CHECKED_BASE(_Last));

}

代码不长,但需要研究才能理解。很多算法都是这样的。

这个算法可以概括为:

如果只有零个或一个元素,返回false,表示回到全排列的起点。

否则,从右边开始,找到第一个不是递减的元素,即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的全排列。

可以很容易地看到,如果把每个排列看成一个数,那么下一个排列大于上一个排列。

算法的关键:从E(i+1)一直到E(n)都是不增的。

这个特性说明,这一范围的元素的排列已经达到最大值,下一个排列必须是找到这一范围内大于这一范围的前一元素的元素,交换这两个元素,交换后E[i+1, 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,布布扣,bubuko.com

时间: 2024-08-19 07:45:21

打印全排列和stl::next_permutation的相关文章

白话 STL next_permutation 原理

翻译自stackoverflow 英语好的同学可以自己去看一下. 什么是next permutation 下面是四个元素{1,2,3,4}的排列 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 ... 每一行都是一个排列. 我们如何从一个排列转到下一个排列呢?我们可以将如上4个数的排列当做一个数.每一个数的下一个排列就是发现下一个比它大的数. next_permutation就是寻找这些元素所组成的数字的升序排列中的下一个数. 比较

STL next_permutation 全排列

调用方法: int arr[4]={1,2,3,4}; while(next_permutation(arr,arr+4)){ for(int i=0;i<4;i++) printf("%d ",arr[i]); puts(""); } 测试效果: 注:可以看到1 2 3 4这个结果被跳过了. 原文地址:https://www.cnblogs.com/TQCAI/p/8455316.html

STL next_permutation排列

概念 全排列的生成算法有很多种,有递归遍例,也有循环移位法等等.C++/STL中定义的next_permutation和prev_permutation函数则是非常灵活且高效的一种方法,它被广泛的应用于为指定序列生成不同的排列.本文将详细的介绍prev_permutation函数的内部算法. 按照STL文档的描述,next_permutation函数将按字母表顺序生成给定序列的下一个较大的序列,直到整个序列为减序为止.prev_permutation函数与之相反,是生成给定序列的上一个较小的序列

STL next_permutation 算法原理和自行实现

目标 STL中的next_permutation 函数和 prev_permutation 两个函数提供了对于一个特定排列P,求出其后一个排列P+1和前一个排列P-1的功能. 这里我们以next_permutation 为例分析STL中实现的原理,prev_permutation 的原理与之类似,我们在最后给出它们实现上差异的比较 问题: 给定一个排列P,求出其后一个排列P+1是什么. 思路 按照字典序的定义不难推出,正序,是一组排列中最小的排列,而逆序,则是一组排列中最大的排列. 从字典序的定

CF459C Pashmak and Buses 打印全排列

这题如果将最终的结果竖着看,每一列构成的数可以看成是k进制的数,一共有d列,任意两列都不相同,所以这就是一个d位k进制数全排列的问题,一共有k ^ d个排列,如果k ^ d < n,则打印-1. 打印最终结果时设第一列就为1 1 1 1 ... 1,然后依次每列增加1后(公交车编号从1开始,不是从0开始) ,注意,这里是k进制. #include <stdlib.h> #include <stdio.h> #include <algorithm> #include

打印全排列思路

从n个不同的元素中取m个元素(m<=n),按照一定的顺序排列起来, 叫做从n个不同元素取出m个元素的一个排列. 当m=n时,所有的排列情况叫做全排列,比如3的全排列为: 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 我们先从简单的开始,要求写出代码打印上面的排列情况即可,顺序可以不一致. 分析过程: 首先,我们如何把三位的数字打印出来呢,有两种方式: printf("%d\n" ,num); //num=123 第二种: printf("%d%

STL next_permutation和prev_permutation函数

利用next_permutation实现全排列升序输出,从尾到头找到第一个可以交换的位置, 直接求到第一个不按升序排列的序列. 1 #include <iostream> 2 #include <algorithm> /// next_permutation, sort 3 #define MAX 100 4 using namespace std; 5 6 int main() { 7 int myints[MAX],n; 8 cin >> n; 9 for (int

C++ STL next_permutation函数

在STL中,除了next_permutation外,还有一个函数prev_permutation,两者都是用来计算排列组合的函数.前者是求出下一个排列组合,而后者是求出上一个排列组合.所谓"下一个"和"上一个",书中举了一个简单的例子:对序列 {a, b, c},每一个元素都比后面的小,按照字典序列,固定a之后,a比bc都小,c比b大,它的下一个序列即为{a, c, b},而{a, c, b}的上一个序列即为{a, b, c},同理可以推出所有的六个序列为:{a,

hdu 1716 排列2(stl next_permutation)

http://acm.hdu.edu.cn/showproblem.php?pid=1716 考到题目直接套 next_permutation 没有注意到0不能为首位 结果wa了一整天 输出结构也略有些小坑 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> using namespace std; int num[10