枚舉排列的三種算法


想不想打印所有排列?

輸入整數n,按字典序從小到大輸出前n個數的所有排列。

1 生成1~n的排列

偽代碼:

void print_permutation(序列A,集合S)
{
    if(S為空) 輸出序列A;
    else按照從小到大的順序依次考慮S中的每個元素
    {
        print_permutation(在A的末尾添加v后得到的新序列,S-{v});
    }
}

下面考慮程序實現。用數組A表示序列A,集合S 不用保存,因為它可以有序列A 完全確定-A中沒有出現的元素都可以選。

代碼:

 1 void print_permutation(int n,int*A,int cur)
 2 {
 3     if(cur==n)//遞歸邊界
 4     {
 5         for(int i = 0;i<n;i++)
 6         {
 7             cout << A[i] << " ";
 8         }
 9         cout << endl;
10     }
11     else
12     {
13         for(int i = 1;i<=n;i++)//嘗試在A中填入各種整數i
14         {
15             int ok = 1;
16             for(int j = 0;j<cur;j++)
17             {
18                 if(A[j]==i)//如果i已經在A[0]~A[cur-1]出現過,則不能再選
19                 {
20                     ok = 0;
21                 }
22             }
23             if(ok)
24             {
25                 A[cur++] = i;
26                 print_permutation(n,A,cur);//遞歸調用
27                 cur--;
28             }
29         }
30     }
31 
32 }

循環變量i是當前考察的A[cur],上面的程序用到了一個標志變量ok,來看i有沒有在A中出現過,ok為1說明沒有出現就可以把i加到A[cur]處

聲明一個足夠大的數組A,然后調用print_permutation(n,A,0);

2 生成可重集的序列

把問題改成輸入數組P,並按字典序輸出數組A各元素的所有全排列,則需要修改if(A[j]==i)為if(A[j]==P[i]),把A[cur]=i,改成A[cur]=P[i]。

但是如果P中有重復元素就會失效,因為上面的算法是看有無重合元素來判斷P[i]在A[j]中出現過沒有,來決定是不是添加P[i];

可以統計A[0]~A[cur-1]中P[i]出現的次數c1,和P[i]在P數組中出現的次數c2,只要c1<c2,就能遞歸調用。

代碼:

void print_permutation_2(int n,int*P,int*A,int cur)
{
    if(cur==n)
    {
        for(int i = 0;i<n;i++)
        {
            cout << A[i] << " ";
        }
        cout << endl;
    }
    else
    {
        for(int i = 0;i<n;i++)
        {
            if(!i||P[i]!=P[i-1])
            {
                int c1 = 0,c2 = 0;
                for(int j = 0; j<cur; j++)
                    if(A[j]==P[i])
                        c1++;
                for(int j = 0; j<n; j++)
                    if(P[i]==P[j])
                        c2++;
                int ok = 1;
                if(c1<c2)
                {
                    A[cur++] = P[i];
                    print_permutation_2(n,P,A,cur);
                    cur--;
                }
            }
        }
    }

}

3 解答樹

遞歸函數的調用可用解答樹來表示,

第0層有n個孩子,第一層n-1個孩子,第二層n-2個孩子,...,第n層為葉節點沒有孩子,每個葉子對應於一個排列,共n!個葉子。

如果某些問題的解可由多個步驟得到,而每個步驟都有若干種選擇,可用遞歸算法實現,他的工作方式可由解答樹描述。

一個重要結論:在多數情況下,解答樹上的節點幾乎全部來自於最后一兩層。和他們相比,上面的節點數可以忽略不計。

4 下一個排列

代碼:

 1 void print_permutation_3(int n,int* P)
 2 {
 3     sort(P,P+n);
 4     do{
 5         for(int i = 0;i<n;i++)
 6         {
 7             cout << P[i] << " ";
 8         }
 9         cout << endl;
10     }while(next_permutation(P,P+n));
11 }

 代碼匯總:

  1 #include <iostream>
  2 #include <algorithm>
  3 #define max_n 10005
  4 using namespace std;
  5 int A[max_n];
  6 int P[max_n];
  7 /*void print_permutation(序列A,集合S)
  8 {
  9     if(S為空) 輸出序列A;
 10     else按照從小到大的順序依次考慮S中的每個元素
 11     {
 12         print_permutation(在A的末尾添加v后得到的新序列,S-{v});
 13     }
 14 }*/
 15 void print_permutation(int n,int*A,int cur)
 16 {
 17     if(cur==n)//遞歸邊界
 18     {
 19         for(int i = 0;i<n;i++)
 20         {
 21             cout << A[i] << " ";
 22         }
 23         cout << endl;
 24     }
 25     else
 26     {
 27         for(int i = 1;i<=n;i++)//嘗試在A中填入各種整數i
 28         {
 29             int ok = 1;
 30             for(int j = 0;j<cur;j++)
 31             {
 32                 if(A[j]==i)//如果i已經在A[0]~A[cur-1]出現過,則不能再選
 33                 {
 34                     ok = 0;
 35                 }
 36             }
 37             if(ok)
 38             {
 39                 A[cur++] = i;
 40                 print_permutation(n,A,cur);//遞歸調用
 41                 cur--;
 42             }
 43         }
 44     }
 45 
 46 }
 47 void print_permutation_2(int n,int*P,int*A,int cur)
 48 {
 49     if(cur==n)
 50     {
 51         for(int i = 0;i<n;i++)
 52         {
 53             cout << A[i] << " ";
 54         }
 55         cout << endl;
 56     }
 57     else
 58     {
 59         for(int i = 0;i<n;i++)
 60         {
 61             if(!i||P[i]!=P[i-1])
 62             {
 63                 int c1 = 0,c2 = 0;
 64                 for(int j = 0; j<cur; j++)
 65                     if(A[j]==P[i])
 66                         c1++;
 67                 for(int j = 0; j<n; j++)
 68                     if(P[i]==P[j])
 69                         c2++;
 70                 int ok = 1;
 71                 if(c1<c2)
 72                 {
 73                     A[cur++] = P[i];
 74                     print_permutation_2(n,P,A,cur);
 75                     cur--;
 76                 }
 77             }
 78         }
 79     }
 80 
 81 }
 82 void print_permutation_3(int n,int* P)
 83 {
 84     sort(P,P+n);
 85     do{
 86         for(int i = 0;i<n;i++)
 87         {
 88             cout << P[i] << " ";
 89         }
 90         cout << endl;
 91     }while(next_permutation(P,P+n));
 92 }
 93 int main()
 94 {
 95     int P[]={1,2,4,3};
 96     //sort(P,P+4);
 97     //print_permutation_2(4,P,A,0);
 98     print_permutation_3(4,P);
 99     return 0;
100 }
View Code

 


免責聲明!

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



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