白话 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就是寻找这些元素所组成的数字的升序排列中的下一个数。

  比较绕口,可以看个例子

1 4 2 3 = 14231 4 3 2 = 1432
2 1 3 4 = 2134
 ....

  1 4 3 2 的next_permutation就是 升序排列中下一个比它大的数2134

如何寻找next_permutation

  还是用1432位例子,现在它是用{1,2,3,4}所能组成的以1开头的最大的四位数。我们想要找到排序中下一个比它大的数该如何呢?

  就是用下一个比1大的元素替代1(就是2),然后加上剩下的三个元素的所能排出的最小数值(也就是134)

  这样就发下了1432的next_permutation 2134

  过程如下:

    1.寻找到1的右边第一个比1大的元素

      1 4 3 2 ----> 2 4 3 1 (注意:这时候的右边三个元素是递减的,我们要求右边三个元素所能排成的最小数值时,只需要将431逆序就可以了)

    2.求出右边元素所能组成的最小排列      

      2 4 3 1 ---->2 1 3 4 <---next_permutation of 1 4 3 2

如何判断一个数应该加入到排列中呢

  那么,我们怎么知道1432是1开头的最大的数呢?观察可以发现,当1的右边也就是432是递减的时候!

  也就是,在一个元素的最右边的元素所排成的序列是递减的时候!

阅读源码

  next_permutation的源码见下面,这里只摘录最重要的部分

while (true)
{
    It j = i;
    i--;
    if (*i < *j) {
        //something..
    }
    if (i == begin) {
        //something
    }
}

  因为i的初值是end,j是i的后一个元素。那么这个while循环可以解释为

while (true)
{
    1如果i的右边是降序,那么做一些事情,返回
    2如果i是开头,那么这就是最后一个排列,做一些事,返回。

    继续循环,递减i j 最后总会符合上面条件中的一个
}

  首先解释2,如果i已经是开头了,那么表示这个排列已经完全是递减的(也就是 4,3,2,1)那么我们将整个数组逆序(得到(1,2,3,4),得到排列的初值。

  现在解释1,如果i的右边已经都是降序了,这时候*i < *j (比如i=0,j=1 (1,4,3,2)) 。i代表右边都是降序的最小的下标值。

  这时候,我们需要做的就是从右边,找到第一个大于i的值,然后将他和i交换,然后再将右边的整体逆序。

  代码如下:

It k = end;

while (!(*i < *--k))
    /* pass */;

iter_swap(i, k);
reverse(j, end);
return true;

附:next_permutation代码

#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

template<typename It>
bool next_permutation(It begin, It end)
{
        if (begin == end)
                return false;

        It i = begin;
        ++i;
        if (i == end)
                return false;

        i = end;
        --i;

        while (true)
        {
                It j = i;
                --i;

                if (*i < *j)
                {
                        It k = end;

                        while (!(*i < *--k))
                                /* pass */;

                        iter_swap(i, k);
                        reverse(j, end);
                        return true;
                }

                if (i == begin)
                {
                        reverse(begin, end);
                        return false;
                }
        }
}

int main()
{
        vector<int> v = { 1, 2, 3, 4 };

        do
        {
                for (int i = 0; i < 4; i++)
                {
                        cout << v[i] << " ";
                }
                cout << endl;
        }
        while (::next_permutation(v.begin(), v.end()));
}

白话 STL next_permutation 原理

时间: 2024-11-05 13:03:58

白话 STL next_permutation 原理的相关文章

打印全排列和stl::next_permutation

打印全排列是个有点挑战的编程问题.STL提供了stl::next_permutation完美的解决了这个问题. 但是,如果不看stl::next_permutation,尝试自己解决,怎么做? 很自然地,使用递归的办法: 1. 单个元素的排列只有1个. 2. 多个元素的排列可以转化为: 以每个元素为排列的首个元素,加上其他元素的排列. 有了思路,就可以编码了. 第一个版本: void printAllPermutations(const std::string& prefix, int set[

STL next_permutation 算法原理和自行实现

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

STL next_permutation排列

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

next_permutation原理剖析

最近刷leetcode的时候遇见next permutation这道题,感觉挺有意思的一个题目,递归的方法是较简单并且容易想到的,在网上搜了其余的解法,就是std::next_permutation非递归解法,但是让人不是很舒服的就是关于原理的部分,千篇一律的都是摘抄<STL源码剖析>,也就是这样的. 在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii.然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元

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,

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

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

hdu1716排列2(stl:next_permutation+优先队列)

排列2 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5437    Accepted Submission(s): 2072 Problem Description Ray又对数字的列产生了兴趣: 现有四张卡片,用这四张卡片能排列出很多不同的4位数,要求按从小到大的顺序输出这些4位数. Input 每组数据占一行,代表四张卡片上的数

POJ 1833 排列【STL/next_permutation】

题目描述: 大家知道,给出正整数n,则1到n这n个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1六个排列. 任务描述: 给出某个排列,求出这个排列的下k个排列,如果遇到最后一个排列,则下1排列为第1个排列,即排列1 2 3…n. 比如:n = 3,k=2 给出排列2 3 1,则它的下1个排列为3 1 2,下2个排列为3 2 1,因此答案为3 2 1. Input 第一行是一个正整数m,表示