【codeup】1959: 全排列 及全排列算法詳解


題目描述

給定一個由不同的小寫字母組成的字符串,輸出這個字符串的所有全排列。
我們假設對於小寫字母有'a' < 'b' < ... < 'y' < 'z',而且給定的字符串中的字母已經按照從小到大的順序排列。

輸入

輸入只有一行,是一個由不同的小寫字母組成的字符串,已知字符串的長度在1到6之間。

輸出

輸出這個字符串的所有排列方式,每行一個排列。要求字母序比較小的排列在前面。字母序如下定義:
已知S = s1s2...sk , T = t1t2...tk,則S < T 等價於,存在p (1 <= p <= k),使得
s1 = t1, s2 = t2, ..., sp - 1 = tp - 1, sp < tp成立。

注意每組樣例輸出結束后接一個空行。

樣例輸入

xyz

樣例輸出

xyz
xzy
yxz
yzx
zxy
zyx

提示

 

用STL中的next_permutation會非常簡潔。

 

 

思路:

由於題目提示使用next_permutation會簡潔,所以這里我們使用此方法。

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<queue>
 4 #include<string>
 5 #include<string.h>
 6 #include<algorithm>
 7 using namespace std;
 8 
 9 char a[10];
10 
11 int main()
12 {
13     int n;
14     while(scanf("%s",a)!=EOF)
15     {
16         n=strlen(a);
17         do
18         {
19             printf("%s\n",a);
20         }while(next_permutation(a,a+n));
21         puts("");
22     }
23     return 0;
24 }

C++/STL中定義的next_permutation和prev_permutation函數是非常靈活且高效的一種方法,它被廣泛的應用於為指定序列生成不同的排列。

next_permutation函數將按字母表順序生成給定序列的下一個較大的排列,直到整個序列為降序為止。

prev_permutation函數與之相反,是生成給定序列的上一個較小的排列。

所謂“下一個”和“上一個”,舉一個簡單的例子:

  對序列 {a, b, c},每一個元素都比后面的小,按照字典序列,固定a之后,a比bc都小,c比b大,它的下一個序列即為{a, c, b},而{a, c, b}的上一個序列即為{a, b, c},同理可以推出所有的六個序列為:{a, b, c}、{a, c, b}、{b, a, c}、{b, c, a}、{c, a, b}、{c, b, a},其中{a, b, c}沒有上一個元素,{c, b, a}沒有下一個元素。

二者原理相同,僅遍例順序相反,這里僅以next_permutation為例介紹算法。

(1) int 類型的next_permutation

復制代碼
int main()
{
    int a[3];
    a[0]=1;a[1]=2;a[2]=3;
    do
    {
        cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
    }while (next_permutation(a,a+3)); //參數3指的是要進行排列的長度

//如果存在a之后的排列,就返回true。如果a是最后一個排列沒有后繼,返回false,每執行一次,a就變成它的后繼
}
復制代碼

輸出:
 1 2 3
 1 3 2
 2 1 3
 2 3 1
 3 1 2
 3 2 1

如果改成

 1 while(next_permutation(a,a+2)); 

則輸出:
 1 2 3
 2 1 3
只對前兩個元素進行字典排序

顯然,如果改成

 1 while(next_permutation(a,a+1));  

則只輸出:1 2 3
 
若排列本來就是最大的了沒有后繼,則next_permutation執行后,會對排列進行字典升序排序,相當於循環
 

1 int list[3]={3,2,1};
2 next_permutation(list,list+3);
3 cout<<list[0]<<" "<<list[1]<<" "<<list[2]<<endl;

輸出: 1 2 3
 
(2) char 類型的next_permutation
 

復制代碼
int main()
{
    char ch[205];
    cin >> ch;
 
    sort(ch, ch + strlen(ch) );
    //該語句對輸入的數組進行字典升序排序。如輸入9874563102
    cout<<ch;//將輸出0123456789,這樣就能輸出全排列了
 
    char *first = ch;
    char *last = ch + strlen(ch);
 
    do {
        cout<< ch << endl;
    }while(next_permutation(first, last));
     return 0;
}
 
//這樣就不必事先知道ch的大小了,是把整個ch字符串全都進行排序
//若采用 while(next_permutation(ch,ch+5)); 如果只輸入1562,就會產生錯誤,因為ch中第五個元素指向未知
//若要整個字符串進行排序,參數5指的是數組的長度,不含結束符
復制代碼

(3) string 類型的next_permutation
 

復制代碼
int main()
{
    string line;
    while(cin>>line&&line!="#")
    {
        if(next_permutation(line.begin(),line.end())) //從當前輸入位置開始
        cout<<line<<endl;
        else cout<<"Nosuccesor\n";
    }
}
復制代碼
復制代碼
int main()
{
    string line;
    while(cin>>line&&line!="#")
    {
        sort(line.begin(),line.end());//全排列
        cout<<line<<endl;
        while(next_permutation(line.begin(),line.end()))
        cout<<line<<endl;
    }
}
復制代碼

 

 next_permutation 自定義比較函數
 

復制代碼
#include<iostream> //poj 1256 Anagram
#include<cstring>
#include<algorithm>
using namespace std;
int cmp(char a,char b) //'A'<'a'<'B'<'b'<...<'Z'<'z'.
{
    if(tolower(a)!=tolower(b))
        return tolower(a)<tolower(b);
    else
        return a<b;
}
int main()
{
    char ch[20];
    int n;
    cin>>n;
    while(n--)
    {
        scanf("%s",ch);
        sort(ch,ch+strlen(ch),cmp);
        do
        {
            printf("%s\n",ch);
        }while(next_permutation(ch,ch+strlen(ch),cmp));
    }
    return 0;
}
復制代碼

 

用next_permutation和prev_permutation求排列組合很方便,但是要記得包含頭文件#include <algorithm>。

雖然最后一個排列沒有下一個排列,用next_permutation會返回false,但是使用了這個方法后,序列會變成字典序列的第一個,如cba變成abc。prev_permutation同理。

 

 

 

 

 

全排列生成算法

對於給定的集合A{a1,a2,...,an},其中的n個元素互不相同,如何輸出這n個元素的所有排列(全排列)。

遞歸算法

這里以A{a,b,c}為例,來說明全排列的生成方法,對於這個集合,其包含3個元素,所有的排列情況有3!=6種,對於每一種排列,其第一個元素有3種選擇a,b,c,對於第一個元素為a的排列,其第二個元素有2種選擇b,c;第一個元素為b的排列,第二個元素也有2種選擇a,c,……,依次類推,我們可以將集合的全排列與一棵多叉樹對應。如下圖所示

在此樹中,每一個從樹根到葉子節點的路徑,就對應了集合A的一個排列。通過遞歸算法,可以避免多叉樹的構建過程,直接生成集合A的全排列,代碼如下。

 

 1 template <typename T>
 2 inline void swap(T* array, unsigned int i, unsigned int j)
 3 {
 4     T t = array[i];
 5     array[i] = array[j];
 6     array[j] = t;
 7 }
 8 
 9 /*
10  * 遞歸輸出序列的全排列
11  */
12 void FullArray(char* array, size_t array_size, unsigned int index)
13 {
14     if(index >= array_size)
15     {
16         for(unsigned int i = 0; i < array_size; ++i)
17         {
18             cout << array[i] << ' ';
19         }
20 
21         cout << '\n';
22 
23         return;
24     }
25 
26     for(unsigned int i = index; i < array_size; ++i)
27     {
28         swap(array, i, index);
29 
30         FullArray1(array, array_size, index + 1);
31 
32         swap(array, i, index);
33     }
34 }

 

 
該算法使用原始的集合數組array作為參數代碼的28~32行,將i位置的元素,與index位置的元素交換的目的是使得array[index + 1]到array[n]的所有元素,對應當前節點的后繼結點,遞歸調用全排列生成函數。調用結束之后還需要回溯將交換位置的元素還原,以供其他下降路徑使用。

字典序

全排列生成算法的一個重要思路,就是將集合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]<a[k+1](0<k<n-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]在字典序中的下一個排列。

這里我們以排列a[1...8]=13876542為例,來解釋一下上述算法。首先我們發現,1(38)76542,括號位置是第一處滿足a[k]<a[k+1]的位置,此時k=2。所以我們在a[3...8]的區間內尋找比a[2]=3大的最小元素,找到a[7]=4滿足條件,交換a[2]和a[7]得到新排列14876532,對於此排列的3~8區間,反轉該區間的元素,將a[3]-a[8],a[4]-a[7],a[5]-a[6]分別交換,就得到了13876542字典序的下一個元素14235678。下面是該算法的實現代碼

 

 1 /*
 2  * 將數組中的元素翻轉
 3  */
 4 inline void Reverse(unsigned int* array, size_t array_size)
 5 {
 6     for(unsigned i = 0; 2 * i < array_size - 1; ++i)
 7     {
 8         unsigned int t = array[i];
 9         array[i] = array[array_size - 1 - i];
10         array[array_size - 1 - i] = t;
11     }
12 }
13 
14 inline int LexiNext(unsigned int* lexinum, size_t array_size)
15 {
16     unsigned int i, j, k, t;
17 
18     i = array_size - 2;
19 
20     while(i != UINT_MAX && lexinum[i] > lexinum[i + 1])
21     {
22         --i;
23     }
24 
25     //達到字典序最大值
26     if(i == UINT_MAX)
27     {
28         return 1;
29     }
30 
31     for(j = array_size - 1, k = UINT_MAX; j > i; --j)
32     {
33         if(lexinum[j] > lexinum[i])
34         {
35             if(k == UINT_MAX)
36             {
37                 k = j;
38             }
39             else
40             {
41                 if(lexinum[j] < lexinum[k])
42                 {
43                     k = j;
44                 }
45             }
46         }
47     }
48 
49     t = lexinum[i];
50     lexinum[i] = lexinum[k];
51     lexinum[k] = t;
52 
53     Reverse(lexinum + i + 1, array_size - i - 1);
54     return 0;
55 }
56 
57 /*
58  * 根據字典序輸出排列
59  */
60 inline void ArrayPrint(const char* array, size_t array_size, const unsigned int* lexinum)
61 {
62     for(unsigned int i = 0; i < array_size; ++i)
63     {
64         cout << array[lexinum[i]] << ' ';
65     }
66 
67     cout << '\n';
68 }
69 
70 /*
71  * 基於逆序數的全排列輸出
72  */
73 void FullArray(char* array, size_t array_size)
74 {
75     unsigned int lexinumber[array_size];
76 
77     for(unsigned int i = 0; i < array_size; ++i)
78     {
79         lexinumber[i] = i;
80     }
81 
82     ArrayPrint(array, array_size, lexinumber);
83 
84     while(!LexiNext(lexinumber, array_size))
85     {
86         ArrayPrint(array, array_size, lexinumber);
87     }
88 }

 

使用字典序輸出集合的全排列需要注意,因為字典序涉及兩個排列之間的比較,對於元素集合不方便比較的情況,可以將它們在數組中的索引作為元素,按照字典序生成索引的全排列,然后按照索引輸出對應集合元素的排列,示例代碼使用的就是此方法。對於集合A{a,b,c,d},可以對其索引1234進行全排列生成。這么做還有一個好處,就是對於字典序全排列生成算法,需要從字典序最小的排列開始才能夠生成集合的所有排列,如果原始集合A中的元素不是有序的情況,字典序法將無法得到所有的排列結果,需要對原集合排序之后再執行生成算法,生成索引的全排列,避免了對原始集合的排序操作。

字典序算法還有一個優點,就是不受重復元素的影響。例如1224,交換中間的兩個2,實際上得到的還是同一個排列,而字典序則是嚴格按照排列元素的大小關系來生成的。對於包含重復元素的輸入集合,需要先將相同的元素放在一起,以集合A{a,d,b,c,d,b}為例,如果直接對其索引123456進行全排列,將不會得到想要的結果,這里將重復的元素放到相鄰的位置,不同元素之間不一定有序,得到排列A'{a,d,d,b,b,c},然后將不同的元素,對應不同的索引值,生成索引排列122334,再執行全排列算法,即可得到最終結果。

 
 
Steinhaus-Johnson-Trotter算法是一種基於最小變換的全排列生成算法,對於排列a[1...n],該算法通過將a[i],與a[i-1](或a[i+1])進行交換,生成下一個排列,直到所有排列生成完畢為止,這樣,當前排列與其后繼排列只是兩個相鄰位置的元素發生了調換。當然,為了防止重復生成某一個排列,算法並非隨意調換某兩個元素之間的位置,其生成全排列的具體規則如下。
  • 首先,以字典序最小的排列起始,並且為該排列的每個元素賦予一個移動方向,初始所有元素的移動方向都向左。
  • 在排列中查找這樣的元素,該元素按照其對應的移動方向移動,可以移動到一個合法位置,且移動方向的元素小於該元素,在所有滿足條件的元素中,找到其中的最大者。
  • 將該元素與其移動方向所對應的元素交換位置。
  • 對於排列中,所有元素值大於該元素的元素,反轉其移動方向。

這里有幾個概念需要說明一下,所謂合法位置,是指該元素按照其移動方向移動,不會移動到排列數組之外,例如對於<4,<1,<2,<3,此時對於元素4,如果繼續向左移動,就會超過數組范圍,所以4的下一個移動位置是非法位置。而且,所有元素,都只能向比自己小的元素的方向移動,如上面例子中的元素2,3,而元素1是不能夠移動到元素4的位置的。每次移動,都要對可以移動的所有元素中的最大者進行操作,上例中元素1,4不能移動,2,3都存在合法的移動方案,此時需要移動3,而不能移動2。合法移動之后,需要將所有大於移動元素的元素的移動方向反轉,上例中的元素3移動后的結果是4>,1<,<3,<2,可以看到,元素4的移動方向改變了。再如此例子<2,<1,3>,4>,對於其中的元素2,4,其對應的下一個移動位置都是非法位置,而對於元素1,3,其下一個移動位置的元素,都比他們要大,對於該排列就找不到一個可以的移動方案,這說明該算法已經達到終態,全排列生成結束。下面是該算法的代碼

 

 1 inline int SJTNext(unsigned int* index, size_t array_size, int* move)
 2 {
 3     unsigned int i, j, t;
 4 
 5     //找到最大合法移動的元素索引
 6     for(i = array_size - 1, j = array_size; i != UINT_MAX; --i)
 7     {
 8         if(i + move[i] < array_size && index[i] > index[i + move[i]])
 9         {
10             if(j == array_size)
11             {
12                 j = i;
13                 continue;
14             }
15 
16             if(index[i] > index[j])
17             {
18                 j = i;
19             }
20         }
21     }
22 
23     //未發現合法的移動策略
24     if(j == array_size)
25     {
26         return 1;
27     }
28 
29     t = index[j];//要交換位置的元素
30     i = j + move[j];//發生交換的位置
31     swap(index, i, j);
32     swap(move, i, j);
33 
34     //將所有比t大的元素的移動方向反轉
35     for(i = 0; i < array_size; ++i)
36     {
37         if(index[i] > t)
38         {
39             move[i] = -move[i];
40         }
41     }
42 
43     return 0;
44 }
45 
46 /*
47  * 基於最小變換的Steinhaus–Johnson–Trotter算法
48  */
49 void FullArray(char* array, size_t array_size)
50 {
51     unsigned int index[array_size];
52     int move[array_size];
53 
54     for(unsigned int i = 0; i < array_size; ++i)
55     {
56         index[i] = i;
57         move[i] = -1;
58     }
59 
60     ArrayPrint(array, array_size, index);
61 
62     while(!SJTNext(index, array_size, move))
63     {
64         ArrayPrint(array, array_size, index);
65     }
66 }

 

代碼使用了一個伴隨數組move標記對應位置元素的移動方向,在元素移動時,move數組中的對應元素也要相應移動。該算法從初始排列<1,<2,<3,<4開始,可以生成4元素的所有排列,直至最終排列<2,<1,3>,4>為止,其狀態轉移如下圖所示,該圖片來自於Wiki百科。

實際上該算法是Shimon Even對於Steinhaus-Johnson-Trotter三人提出的全排列生成算法的改進算法,在算法中實際上還有一個問題需要解決,就是對於給定的排列,如何判斷其所有元素的移動方向,如果上面所謂終態的移動方向是<2,<1,3>,<4,那么這個狀態就還存在可行的移動方案。Johnson(1963)給出了判斷當前排列各元素移動方向的方法,對於排列中的每個元素,判斷所有比該元素小的元素所生成序列的逆序數,如果逆序數為偶,則該元素的移動方向為向左,否則移動方向向右,我們用這條原則來看一下上面的終態2,1,3,4。對於元素1,沒有比1小的元素,此時我們認為,空序列的逆序數為偶,所以元素1的移動方向向左;對於元素2,比2小的元素形成的序列為1,單元素序列的逆序數為偶,所以2的移動方向向左;對於元素3,小於3的元素組成的序列為21,逆序數為1,奇數,所以3的移動方向向右;對於元素4,對應序列為213,逆序數為奇數,所以4的移動方向向右。根據該規則就可以知道,給定某一排列,其對應元素的移動方向是確定的。

 

 

基於階乘數的全排列生成算法,是另一種通過序列順序,輸出全排列的算法。所謂階乘數,實際上和我們常用的2進制,8進制,10進制,16進制一樣,是一種數值的表示形式,所不同的是,上面這幾種進制數,相鄰位之間的進制是固定值,以10進制為例,第n位與第n+1位之間的進制是10,而階乘數,相鄰兩位之間的進制是變值,第n位與第n+1位之間的進制是(n+1)!。對於10進制數,每一位的取值范圍也是固定的0~9,而階乘數每一位的取值范圍為0~n。可以證明,任何一個數量,都可以由一個階乘數唯一表示。下面以23為例,說明其在各種進制中的表現形式

  2進制 8進制 10進制 16進制 階乘數
23 10111 27 23 17 3210

 

其中10進制23所代表的數量的計算方法為

D(23) = 2×10^1 + 3×10^0 = 2×10 + 3×1 = 23

階乘數3210所代表的數量的計算方法為

F(3210) = 3×3! + 2×2! + 1×1! + 0×0! = 3×6 + 2×2 + 1×1 + 1×0 = 23

對於階乘數而言,由於階乘的增長速度非常快,所以其可以表示的數值的范圍隨着位數的增長十分迅速,對於n位的階乘數而言,其表示的范圍從0~(n+1)!-1,總共(n+1)!個數。階乘數有很多性質這里我們只介紹其和全排列相關的一些性質。

首先是加法操作,與普通十進制數的加法基本一樣,所不同的是對於第n位F[n](最低位從第0位開始),如果F[n]+1>n,那么我們需要將F[n]置0,同時令F[n+1]+1,如果對於第n+1位,也導致進位,則向高位依次執行進位操作。這里我們看一下F(3210)+1,對於第0位,有F[0]+1=0+1=1>0,所以F[0]=0(實際上階乘數的第0位一直是0),F[1]+1=1+1=2>1,F[1]=0,……,依次執行,各位都發生進位,最終結果F(3210)+1=F(10000)。

其次,對於n位的階乘數,每一個階乘數的各位的數值,正好對應了一個n排列各位的逆序關系。這里以abcd為例。例如F(2110),其對應的排列的意思是,對於排列的第一個元素,其后有兩個元素比他小;第二個元素,后面有一個元素比他小;第三個元素,后面有一個元素比他小。最終根據F(2110)構建的排列為cbda。4位的階乘數,與4排列的對應關系如下表所示。

0000 abcd 1000 bacd 2000 cabd 3000 dabc
0010 abdc 1010 badc 2010 cadb 3010 dacb
0100 acbd 1100 bcad 2100 cbad 3100 dbac
0110 acdb 1110 bcda 2110 cbda 3110 dbca
0200 adbc 1200 bdac 2200 cdab 3200 dcab
0210 adcb 1210 bdca 2210 cdba 3210 dcba

 

由此,我們就可以利用階乘數與排列的對應關系構建集合的全排列,算法如下。

  • 對於n個元素的全排列,首先生成n位的階乘數F[0...n-1],並令F[0...n-1]=0。
  • 每次對F[0...n-1]執行+1操作,所得結果,根據其與排列的逆序對應關系,生成排列。
  • 直到到達F[0...n-1]所能表示的最大數量n!-1為止,全部n!個排列生成完畢。

這里有一個問題需要解決,就是如何根據階乘數,及其與排列逆序的對應關系生成對應的排列,這里給出一個方法,

  • 以字典序最小的排列a[0...n-1]作為起始,令i從0到n-2。
  • 如果F[i]=0,遞增i。
  • 否則令t=a[i+F[i]],同時將a[i...i+F[i]-1]區間的元素,向后移動一位,然后令a[i]=t,遞增i。

下面說明一下如何根據階乘數F(2110)和初始排列abcd,構建對應的排列。首先,我們發現F[0]=2,所以我們要將a[0+2]位置的元素c放在a[0]位置,之前,先用臨時變量t記錄a[2]的值,然后將a[0...0+2-1]區間內的元素向后移動一位,然后令a[0]=t,得到cabd,i值增加1;然后有F[1]=1,所以我們要將a[1+1]=a[2]=b放在a[1]位置,同時將a[1]向后移動一位,得到排列cbad;然后有F[2]=1,所以將a[2+1]=a[3]=d放在a[2]位置,同時a[2]向后移動一位。最終得到cbda,排列生成結束。整個算法代碼如下

 

 

inline int FacNumNext(unsigned int* facnum, size_t array_size)
{
    unsigned int i = 0;

    while(i < array_size)
    {
        if(facnum[i] + 1 <= i)
        {
            facnum[i] += 1;
            return 0;
        }
        else
        {
            facnum[i] = 0;
            ++i;
        }
    }

    return 1;
}

/*
 * 根據階乘數所指定的逆序數根據原始字符串構建排列輸出
 */
inline void BuildPerm(const char* array, size_t array_size, const unsigned int* facnum, char* out)
{
    char t;
    unsigned int i, j;

    memcpy(out, array, array_size * sizeof(char));

    for(i = 0; i < array_size - 1; ++i)
    {
        j = facnum[array_size - 1 - i];

        if(j != 0)
        {
            t = out[i + j];
            memmove(out + i + 1, out + i, j * sizeof(char));
            out[i] = t;
        }
    }
}

/*
 * 基於階乘數(逆序數)的全排列生成算法
 */
void FullArray(char* array, size_t array_size)
{
    unsigned int facnum[array_size];
    char out[array_size];

    for(unsigned int i = 0; i < array_size; ++i)
    {
        facnum[i] = 0;
    }

    BuildPerm(array, array_size, facnum, out);

    for(unsigned int i = 0; i < array_size; ++i)
    {
        cout << out[i] << ' ';
    }

    cout << '\n';

    while(!FacNumNext(facnum, array_size))
    {
        BuildPerm(array, array_size, facnum, out);

        for(unsigned int i = 0; i < array_size; ++i)
        {
            cout << out[i] << ' ';
        }

        cout << '\n';
    }
}

 

 

用該算法生成1234全排列,順序如下圖,該圖來自與Wiki百科。

從生成排列順序的角度講,概算法相較於字典序和最小變更有明顯優勢,但是在實際應用中,由於根據階乘數所定義的逆序構建排列是一個O(n^2)時間復雜度的過程,所以算法的整體執行效率遜色不少。但是通過階乘數建立逆序數與排列對應關系的思路,還是十分精彩的,值得借鑒

 

 


免責聲明!

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



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