zoukankan      html  css  js  c++  java
  • 全排列算法思想

    参考于:【STL】next_permutation的原理和使用

    给定一个数列,如何得到它的全排列?

    例如1 2 3,它的全排列是123132213231312321

    全排列的关键在于,给定某一数列,能从该数列推出“下一个”数列。

    那么如何找“下一个”数列呢?

     

    找“下一个”数列的算法思路描述如下:

    1、从后往前找两个相邻元素,前一个位置记为i,后一个记为j,并且满足s[i] < s[j]

    2、从后往前找另一个位置k,若满足s[i] < s[k],则将s[i]s[k]交换,并将j之后(包括j)的所有元素颠倒排序,即得“下一个”数列。

     

    先试着自己按照这个算法思路去模拟一下算法过程,看能不能悟出这个算法为什么是对的?如果还是不明白这个算法为什么是对的,再继续往下看。

     

    举个例子,6 2 5 4 3 1,观察一下,下一个数列是6 3 1 2 4 5,回忆一下观察的过程,你是先从后往前找的对不对?你在找什么?

     

    其实你在从后往前找,比较相邻两个数字,如果前一个数字比后一个数字大或相等,就继续往前找,直到找到前一个数字比后一个数字小,我们将前一个数字的位置记为i,后一个数字的位置记为j,此时应该满足s[i]<s[j]。这个例子中i = 1j = 2

     

    可以看出j开始的数列都是非升序排列的,因为刚刚在找的过程中,前一个数字都是比下一个数字大或相等。

     

    要知道,非升序排列的数列不可能有“下一个”数列,即非升序排列的数列是“最大的”。那么j开始的数列必定不存在“下一个”数列;另外一点,我们需要考虑i之前(不包括i)的数列吗?比如例子中的6,不需要!因为从i开始的数列必定存在“下一个”数列,因为它不是非升序排列的,非升序排列的数列必定存在“下一个”数列。所以结论是,我们只考虑从从i开始的数列找“下一个”数列

     

    j开始的数列不可能有“下一个”数列,那么“下一个”数列的i位置放的值不能是原数,而应该从后面的数找,找比原数大的,然后这个数要尽量小。那么如何找到这个最小的数呢?因为从j开始的数列是非升序,所以从后往前找,找到比原数大的就行了。这个位置,我们记为k,例子中这个最小的数是3,即k=4

     

    找到k之后,交换i,k位置的值(一定可以找到这样的k,因为j位置就满足了)。交换之后,从j开始的数列依然是非升序排列。这个很明显,就不多解释了。

     

    因为从j开始的数列是非升序排列,逆序一下,得到非降序排列,非降序排列是“最小的”。所以交换之后,j开始,逆序排序一下,即得到“下一个”排列。

     

    下面自己实现的源代码(原理同STL中的next_permutation)

    bool nextPermutation(int s[], int first, int last)    //last最后一个元素的下一个位置 
    {
        if (last-first <= 1)    //保证至少2个元素
            return false;
        
        int i = last-2, j = last-1;
        
        while (1)
        {
            if (s[i] < s[j])    //只要存在这样的对,下一个排列就必定存在 
            {
                int k = last;
                
                while (s[i] >= s[--k]);    
                
                swap(s[i], s[k]);
                
                reverse(s+j, s+last);
                
                return true;
            }
            if (i == first)        //找到第一个元素都找不到,说明该数组是降序排列的,下一个排列不存在 
            {
                reverse(s+first, s+last);
                return false;
            }
            i--, j--;
        }
    }
    View Code

    ps.理解源代码之后就可以知道为什么降序排列的数组,用STL中的next_permutation之后变成升序排列

    最后再补充一个不规范的递归版本,所谓不规范,就是排列的顺序与STL中的next_permutation不一样。

    void nextPermutationRecursive(int s[], int n, int first, int last)    //last是最后一个元素的下一个位置 
    {
        if (last-first == 1)    //只含一个元素
        {
            for (int i = 0; i < n; ++i)
                cout << s[i];
            cout << endl;
        }
        else
        {
            for (int i = first; i < last; ++i)
            {
                swap(s[first], s[i]);
                
                nextPermutationRecursive(s, n, first+1, last);
                
                swap(s[first], s[i]);
            }
        }    
    }
    View Code
  • 相关阅读:
    Centos7 keepalived 修改日志路径
    mysql 双主复制 centos7
    CentOs 7 安装mysql5.7.18(二进制版本)
    oracle、mysql新增字段,字段存在则不处理
    mysql+ibatis 批量插入
    oracle+ibatis 批量插入-支持序列自增
    oracle 批量插入-支持序列自增
    sftp上传
    java
    mysql
  • 原文地址:https://www.cnblogs.com/chenyg32/p/3646074.html
Copyright © 2011-2022 走看看