STL中关于全排列next_permutation以及prev_permutation的用法

这两个函数都包含在algorithm库中。STL提供了两个用来计算排列组合关系的算法,分别是next_permutation和prev_permutation。

一、函数原型

首先我们来看看这两个函数的函数原型:

  • next_permutation:
1 template< class BidirIt >bool next_permutation( BidirIt first, BidirIt last );
2 template< class BidirIt, class Compare >bool next_permutation( BidirIt first, BidirIt last, Compare comp );
  • prev_permutation:
1 template< class BidirIt >bool prev_permutation( BidirIt first, BidirIt last);
2 template< class BidirIt, class Compare >bool prev_permutation( BidirIt first, BidirIt last, Compare comp);

1.参数

first,end ——重新排序的元素范围

comp —— 自定义比较函数

顾名思义,next_permutation就是求下一个排列组合,而prev_permutation就是求上一个排列组合。首先我们必须了解什么是“下一个”排列组合,什么是“前一个”排列组合。考虑由三个字符所组成的序列{a,b,c}。

那么按照字典序排升序他们一共有下面这几种排列方式:

  • abc
  • acb
  • bac
  • bca
  • cab
  • cba

如果给定排列方式P,令P为{acb},那么next_permutation即求P+1也就是{bac},prev_permutation也就是求P-1即为{abc}。当然也可以自定义谓词函数进行自定义的“下一个排列组合”。

二、代码演示

下面是示范代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3
 4
 5 int main(){
 6     int a[] = {1,2,3};
 7     do{
 8         for(int i = 0; i < 3; i++) cout << a[i] << ‘ ‘;
 9         cout << endl;
10     }while(next_permutation(a,a+3));
11
12     cout << endl;
13     do{
14         for(int i = 0; i < 3; i++) cout << a[i] << ‘ ‘;
15         cout << endl;
16     }while(prev_permutation(a,a+3));
17     return 0;
18 }

  预计上面代码的运行结果应该是输出两组1,2,3的排列组合方式,共12行,但实际运行结果如下:

Why?

我们试着输出第一个循环后a数组的排列方式,结果会得到1 2 3,这是因为当next_permutation去找下一个排列组合P+1,找到则排列为下一个排列组合返回true,否则数组变成字典序最小的排列组合(即为重置排列)并返回false,prev_permutation也同理。

三、手动实现

我们先想想如何实现next_permutation,prev_permutation 的原理与之类似。根据字典序,如果排列为正序既从小到大排列,是一组排列组合中最小的排列方式,而逆序既从大到小排列,则是一组排列组合中最大的排列方式。

在n个元素的排列全排列中,从给定排列P 求解下一排列P+1 ,假设两个排列组合有前k位是相同的,那么我们只需要在后面n-k个元素的排列 P(n-k)中求得下一个排列即可。既然我们需要的是后面 n-k位的排列,那么直接从后向前考察。

例如排列 1 2 3 4 5,这是一个正序排列,因此全排列中最小的排列,记为P.

现在要求P+1,设P1=P+1,P1是 1 2 3 5 4. 可以发现P1的前三位和P的前三位完全相同,唯一不同的是后两位顺序颠倒,最后两位从正序的 4 5 变成了逆序的 5 4.

接着求P1+1.设P2 = P1+1,P2是 1 2 4 3 5. 因为最后两位已经是逆序,不可能有字典序更大的排列,因此必须考虑更多的位,在后3个元素中,3 5 4 显然不是逆序,所以存在字典序更大的排列方式,我们由此确定了n-k=3

现在要在 3 5 4 中求得下一个排列,3 5 4 不是一个逆序,是因为 3 后面有元素大于3 。所以我们要在大于3的数字中选择最小的那个和3交换,保证得到最小的首位元素。选择将3和4进行交换,而不是3 和 5,这样得到的首位元素是4. 现在我们得到了排列 4 5 3 。

显然,4 5 3 并不是我们想要的下一个排列,下一个排列是 4 3 5. 观察区别,不难看出,首位元素一定是4,但是5 3 这个子排列是一个逆序排列。将逆序排列反向后,得到的正序排列是所能形成的最小排列,因此,4 3 5 是4 为首位元素所能形成最小排列,而前3 位没有变化,故我们得到了下一排列P2.

另外,大于3的最小元素,即4 ,也是第一个大于3的元素,因为 5 4 是个逆序排列。

对于可重集排列 1 2 3 5 4 3 2 1也同理,首先找到第一个非逆序元素,这里是3,然后从后向前寻找第一个大于3的元素,这里是4,交换,得到 4 5 3 3 2 1 的子排列,然后反向,即可得到下一排列。如果没有找到第一个非逆序元素,那么说明该排列已经是最大排列。

代码:

 1 template<class T>
 2 bool next_permutation(T begin, T end){
 3     T i = end;
 4     if (begin == end || begin == --i) return false;
 5
 6     while(1){
 7         T i1 = i,i2;
 8         if (*--i < *i1) {   //找第k位
 9             for(i2 = end; *i >= *i2; i2--);//从后往前找到逆序中大于*i的元素的最小元素
10             iter_swap(i, i2);
11             reverse(i1, end);////将尾部的逆序变成正序
12             return true;
13         }
14         if (i == begin) {
15             reverse(begin, end);
16             return false;
17         }
18     }
19 }

prev_permutation的代码实现也类似,这里不再给出,各位可以自行尝试。

四、复杂度分析

最多n/2次交换,n为区间长度,平均每次调用使用了3次比较和1.5次交换,时间复杂度为O(n)。

原文地址:https://www.cnblogs.com/xenny/p/10192983.html

时间: 2024-10-06 00:45:27

STL中关于全排列next_permutation以及prev_permutation的用法的相关文章

STL中的全排列实现

permutation: 在遇到全排列问题时,在数据量较小的情况下可以使用dfs的做法求得全排列,同时我们也知道在STL中存在函数next_permutation和prev_permutation,这两个函数可以较快的求出全排列,而这两个函数的实现却不是依赖于搜索算法(dfs)的. 分析: 以next_permutation为例,数据以1,2,3,4,5为例,对于一个排列我们知道其按照从小到大排序后的结果就是字典序最小的一个排列,而对于它的下一个排列为1,2,3,5,4比较这两个排列我们发现,排

STL具体操作之next_permutation和prev_permutation函数

 next函数默认的是从小到大的顺序,pre函数默认的是从大到小的顺序: {3,1,2}用next得到的结果是{3,1,2}和{3,2,1}: 用pre得到的结果是{3,1,2},{2,3,1},{2,1,3},{1,3,2,},{1,2,3}: 原理如下: [STL]next_permutation的原理和使用 1.碰到next_permutation(permutation:序列的意思) 今天在TC上碰到一道简单题(SRM531 - Division Two - Level One),是

STL中的全排列(1)未完待续。

STL 题目 I: 擅长排列的ZJ 时间限制: 10 Sec  内存限制: 128 MB 提交: 25  解决: 17 [提交][状态][论坛] 题目描述 一天ZJ看到小明竟然排序学的比自己还牛,于是就出了一道题来难为小明,来证明自己排序学的比小明更牛.题目就是有一个数n(0<n<10),写出1到n的全排列,这时小明有点囧了,,,聪明的你能帮小明解围吗? 输入 第一行输入一个数N(0<N<10),表示有N组测试数据.后面的N行输入多组输入数据,每组输入数据都是一个整数x(0<

STL中的全排列(2)

题目 H: 擅长排列的小明 时间限制: 20 Sec  内存限制: 128 MB 提交: 21  解决: 17 [提交][状态][论坛] 题目描述 小明十分聪明,而且十分擅长排列计算.比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难他,在这5个数字中选出几个数字让他继续全排列,那么你就错了,他同样的很擅长.现在需要你写一个程序来验证擅长排列的小明到底对不对. 输入 第一行输入整数N(1<N<10)表示多少组测试数据, 每组测试数据第一行两个整数 n m (1<n&l

STL中_Rb_tree的探索

我们知道STL中我们常用的set与multiset和map与multimap都是基于红黑树.本文介绍了它们的在STL中的底层数据结构_Rb_tree的直接用法与部分函数.难点主要是_Rb_tree的各个参数的确定. 特别注意在如下代码的Selector类用于从Node中选出用于排序的key值,这个仿函数必须返回const int&而不能是int,否则less<int>::operator(const int&, const int&)会抛出segmentation fa

STL中的next_permutation

给定一个数组a[N],求下一个数组. 2 1 3 4 2 1 4 3 2 3 1 4 2 3 4 1 ..... 在STL中就有这个函数: 1.参数是(数组的第一个元素,数组的末尾),注意这是前闭后开区间,(a,a+n) 2.返回值是bool型,表示这个数组是不是最后一个元素. 3.这个函数不仅可以实现n个互异的数的全排列,也能够生成组合数.如1 2  3 3 4 4 5 6 6这样数组的全排列. 4.第一个数组是升序排列,最后一个数组是降序排列. int a[] = {1,2,4,4}; do

c++ STL中的next_permutation

default (1) template <class BidirectionalIterator> bool next_permutation (BidirectionalIterator first, BidirectionalIterator last); custom (2) template <class BidirectionalIterator, class Compare> bool next_permutation (BidirectionalIterator f

枚举所有排列-STL中的next_permutation

枚举排列的常见方法有两种 一种是递归枚举 另一种是STL中的next_permutation //枚举所有排列的另一种方法就是从字典序最小排列开始,不停的调用"求下一个排列"的过程 #include<cstdio> #include<algorithm> using namespace std; int main() { int n,p[10]; scanf("%d",&n); for(int i=0;i<n;i++) scan

next_permutation( ) 和prev_permutation( ) 全排列函数

头文件#include <algorithm> 两者都是用来计算排列组合的函数.前者是求出下一个排列组合,而后者是求出上一个排列组合. 所谓"下一个"和"上一个",有一个例子; 对序列 {a, b, c}, a > b >c,它的下一个序列即为{a, c, b},而{a, c, b}的上一个序列即为{a, b, c},同理可以推出所有的六个序列为:{a, b, c}.{a, c, b}.{b, a, c}.{b, c, a}.{c, a, b