zoukankan      html  css  js  c++  java
  • 白话 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 = 1423
    1 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()));
    }
  • 相关阅读:
    BBS
    BBS
    BBS
    BBS
    6.1
    Django
    Django
    tomcat——启动项目报错:java.lang.IllegalStateException
    Java——java.lang.NullPointerException
    Java——反射三种方式的效率对比
  • 原文地址:https://www.cnblogs.com/xingzhg/p/3927453.html
Copyright © 2011-2022 走看看