全排列算法思想

参考于:【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
原文地址:https://www.cnblogs.com/chenyg32/p/3646074.html