您的位置:首页 > 其它

【经典算法】STL之next_permutation be52 和prev_permutation

2015-09-08 21:09 330 查看
next_permutation和prev_permutation这两个算法的实现,非常的经典,同样十分的有趣。

第一次听说大约是在大四的时候,听一个准备面试的学长说起。

今天又遇到同事正好问了面试的同学这个问题,遂记录下来。

首先,来弄明白什么是"下一个"排列,或者"上一个"排列。

举个例子,abc组成的一个排列,以字典序来说,abc,acb,bac,bca,cab,cba,一共是六种。

这样abc最小,cba最大。比如说到bac,下一个排列就是bca,上一个排列acb。

先来看看next_permutation

(1)如果采用直接枚举所有的排列,然后找出下一个排列,显得有点笨拙。

但凡说起算法,自然会有巧妙的解法。为此,又仔细去翻看侯捷先生的经典著作《STL源码剖析》。

基本思路:从最末尾向前面找两个相邻的元素i和j,并且满足i<j。然后从最末尾继续向前找第一个大于i的元素k,

将i和k交换,并将j之后的元素颠倒得到的序列即为所求。

描述有点抽象,还是来点形象的。

1743256------->1743265

这里i=5,j=6,第一个比大于i的是k=6。

175432------->275431----->271345

这里i=1,j=7,第一个比大于i的元素k=2。

为什么是这样的?

我们来看下面一个事实

令F为i之前的数,E为j之后小于j的数,E必定为一个从大到小的全排列(形如54321这种,如若不然,必定存在一组数,使得左边小于右边,如53421),

则原来的数FijE。

存在这样的事实,ijE组成的序列中一定存在新的排列比ijE大,因为至少jiE这个数是满足的。

这时从上面FijE最末端寻找一个数k,使得它第一个大于i,这个数可以是j。

1>这里若E中的数均小于i,则k为j,那么显然交换i和E中的某个数,这个新组成的数值小于ijE。

那么只能交换i和j。这时新组成的数jiE,一定大于ijE。因为ji>ij,所以新组成的数jiE,固定前两位ji,找到E中数的最小排列,则是直接将E颠倒即可。

这是因为E是一个从大到小的全排列。

2>这里若E中存在某个数大于i,则将这个数记为k,于是将E记为SkT,S为k之前的数的排列(大于i),T为k之后的数的排列(不大于i)。

k是从最末尾比较,第一个比i大的数。与i交换的数中,k肯定是E中满足大于i最小的那一个。于是,交换之后kj>ij,然后固定kj,求SiT构成的序列的最小排列。

由前述(1)中同理,颠掉SiT,即可。

好了,到这里基本就知道next_permutation的计算方式了。

(2)prev_permutation

知道了next_permutation怎么求,prev_permutation就比较简单了。

基本思路:

从最末尾向前面找两个相邻的元素i和j,并且满足i>j。然后从最末尾继续向前找第一个小于i的元素k,

将i和k交换,并将j之后的元素颠倒得到的序列即为所求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 stl