全排列算法思想


參考於:【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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM