快速瞭解全排列

全排列問題

在這裏插入圖片描述

遞歸分析

•用一個數組a[n]來保存1n之間的n個自然數,對於i=1n,每次用a[1]與a[i]交換後,對a[2]~a[n]中的n-1個元素進行全排列

說明:大家輪流來做第1個 ,→ 循環 + 交換。確定好第1個後,對剩下的n-1個再進行全排列→遞歸

•然後交換a[1]與a[i]的值,使它恢復到此次排列前的狀態

已經做過第一個了,請回到原地,輪到下一個當第一個了。

確定好了第一個之後,從剩下的n-1箇中輪流選一個來做第2個 → 循環 + 交換 ,確定好了第二個之後,從剩下的n-2個再進行全排列。直到只有一個素的時候,全排列就是自己。

inline void perm(int list[], int k, int n)//求list數組 第k個到第n的全排列。
{
    int i, t;
    if (k == n)
    {
        for (i = 0; i <= n; i++)
        {
            printf("%d", list[i]);
        }
        printf("\n");
    }

    for (i = k; i <= n; i++)
    {
        swap(list[k], list[i]);
        perm(list, k + 1, n); //求k+1~n的全排列
        swap(list[k], list[i]);
    }
}
字典序

全排列生成算法的一個重要思路,就是將集合A中的元素的排列,與某種順序建立一一映射的關係,按照這種順序,將集合的所有排列全部輸出。這種順序需要保證,既可以輸出全部的排列,又不能重複輸出某種排列,或者循環輸出一部分排列。字典序就是用此種思想輸出全排列的一種方式。這裏以A{1,2,3,4}來說明用字典序輸出全排列的方法。

首先,對於集合A的某種排列所形成的序列,字典序是比較序列大小的一種方式。以A{1,2,3,4}爲例,其所形成的排列1234<1243,比較的方法是從前到後依次比較兩個序列的對應元素,如果當前位置對應元素相同,則繼續比較下一個位置,直到第一個元素不同的位置爲止,元素值大的元素在字典序中就大於元素值小的元素。上面的a1[1…4]=1234和a2[1…4]=1243,對於i=1,i=2,兩序列的對應元素相等,但是當i=2時,有a1[2]=3<a2[2]=4,所以1234<1243。

使用字典序輸出全排列的思路是,首先輸出字典序最小的排列,然後輸出字典序次小的排列,……,最後輸出字典序最大的排列。這裏就涉及到一個問題,對於一個已知排列,如何求出其字典序中的下一個排列。這裏給出算法。

  • 對於排列a[1…n],找到所有滿足a[k]<ak+1的k的最大值,如果這樣的k不存在,則說明當前排列已經是a的所有排列中字典序最大者,所有排列輸出完畢。
  • 在a[k+1…n]中,尋找滿足這樣條件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是說a[l]>a[k],但是小於所有其他大於a[k]的元素。
  • 交換a[l]與a[k].
  • 對於a[k+1…n],反轉該區間內元素的順序。也就是說a[k+1]與a[n]交換,a[k+2]與a[n-1]交換,……,這樣就得到了a[1…n]在字典序中的下一個排列。

在這裏插入圖片描述

int arry[3] = { 1,2,2 };//len==3;  
      //重複序列會去除
    void Permutation()  
    {  
        int len = 3;  
        int j, k;  
      
        while (true)  
        {  
            printf("%d%d%d\n", arry[0], arry[1], arry[2]);  
      
            for (j = len - 2; j >= 0 && arry[j] >= arry[j + 1]; j--);//注意此處 j >= 0 判斷條件在前,加個等號即可  
      
            if (j < 0) return;//結束  
      
            for (k = len - 1; k > j&&arry[k] <= arry[j]; k--);//加個等號即可  
      
            swap(arry[k], arry[j]);  
      
            for (int l = j + 1, r = len - 1; l < r; l++, r--)  
                swap(arry[l], arry[r]);  
           
        }  
    }  

從原理到應用

我們已經知道了實現原理,現在來說一下使用吧,

不知道大家是否還記得STL—“algorithm" 中的兩個函數next_permutation和prev_permutation

  • next_permutation: 對於當前的排列,如果在字典序中還存在下一個排列,返回真,並且將下一個排列賦予當前排列,如果不存在,就把當前排列進行遞增排序。
  • prev_permutation: 對於當前的排列,如果在字典序中還存在前一個排列,返回真,並且將前一個排列賦予當前排列,如果不存在,就把當前排列進行遞減排序。
    #include<iostream>  
    #include<algorithm>  
      
    using namespace std;  
      
    int arry[3] = { 1,2,3 };//len==3;  
      
    void Permutation()  
    {  
        do  
            printf("%d%d%d\n", arry[0], arry[1], arry[2]);  
        while (next_permutation(arry, arry + 3));  
          
    }  
      
    int main()  
    {  
      
        Permutation();  
      
        return 0;  
    }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章