全排列递归实现的讨论
?給出1, 2, 3, 4四個數(shù), 請編程輸出其全排列, 如:
1 2 3 41 2 4 3
1 3 2 4
1 3 4 2
...
這樣的題, 我們在學(xué)校的時候一般都遇到過,而我們最先能想到的,應(yīng)該就是遞歸實現(xiàn)了,因為這和我們我理解的數(shù)學(xué)中的排列組合比較一致:先取第一個數(shù),有4種可能,再在剩下的3個數(shù)種取出第二個數(shù),這又有3種可能,這樣下去直到取到最后一個數(shù)。 這樣,4個數(shù)的全排列就有4*3*2 = 24個。n個數(shù)的全排列就是n*(n-1)*(n-2)*...*2*1. 按照這個描述, 我們發(fā)現(xiàn)有兩點在程序中遞歸實現(xiàn)時十分重要:
1. 哪些數(shù)已經(jīng)取過了而哪些數(shù)又是沒有取過,可以用的?
2. 現(xiàn)在取的是哪一個數(shù)。
確保了這兩個信息,我們的遞歸實現(xiàn)就沒有什么問題了。對于第一個問題,我們有兩種方法可以實現(xiàn):
1) 用一個對應(yīng)的bool型數(shù)組來記錄被排列數(shù)組的使用狀態(tài),這個狀態(tài)在遞歸過程中需要回溯
2) 用一個ILLEGAL值來表示不是屬于排序的數(shù),排列數(shù)組中的數(shù)一旦被使用,就用這個值來覆蓋,當(dāng)然,遞歸過程中此值也需要回溯。
同樣,現(xiàn)在取得是哪個數(shù),我們也有兩種方法來表示:
1) 用參數(shù)的方式來表明這次遞歸調(diào)用是為了得第幾個值、
2) 用一個靜態(tài)變量來表示當(dāng)前遞歸的深度,此深度值表明了我當(dāng)前取的是哪個數(shù)。
上面兩點的兩種解決方法排列組合一下:),我們就有4種方法
首先是定義最大數(shù)組長度與非法值
#define?N?10#define?ILLEAGALNUM??-100
下面列出每一種實現(xiàn):
//數(shù)組存使用狀態(tài),調(diào)用深度表示取第幾個數(shù)void?Permutation1(int?a[],?int?n)
{
????static?int?out[N];?//?result?array
????static?bool?m[N]?=?{1,1,1,1,1,1,1,1,1,1};?//?mark?array,?indicate?whether?the?coorespond?element?
??????????????????????????????????????????????//in?array?a?is?already?used.
????static?int?depth?=?-1;??????????????????????//recursive?call?depth.
????depth++;
????for(int?i?=?0;?i?<?n;?++i)
????{
????????if(depth?==?n)??????????????????????//?if?we?already?get?the?last?num,?print?it
????????{
????????????static?int?l?=?1;
????????????printf("%3d:?",?l++);
????????????for(int?k?=?0;?k<n;?k++)?printf("%d?",?out[k]);
????????????printf("?");
????????????depth--;
????????????return;
????????}
????????else?if(true?==?m[i])??????????????????????//?if?element?i?not?used
????????{
????????????out[depth]?=?a[i];
????????????m[i]?=?false;??????????????????????//?mark?element?i?as?used
????????????Permutation1(a,?n);??????????????????????//?recursive?to?get?next?num
????????????m[i]?=?true;??????????????????????//?backdate?,?so?that?we?can?try?another?case
????????}
????}
????depth--;
}
//修改數(shù)據(jù)數(shù)組表示其使用狀態(tài),參數(shù)表示取第幾個數(shù)
void?Permutation2(int?a[],?int?index,?int?n)
{
????static?int?out[N];
????for(int?i?=?0;?i?<?n;?++i)
????{
????????if(index?==?n)??????????????????????//index?>?n-1,?try?to?get?the?n-1?num,?means?it?is?ok?,?printf?it
????????{
????????????static?int?l?=?1;
????????????printf("%3d:?",?l++);
????????????for(int?k?=?0;?k<n;?k++)?printf("%d?",?out[k]);
????????????printf("?");
????????????return;
????????}
????????else?if(a[i]?!=?ILLEAGALNUM)
????????{
????????????out[index]?=?a[i];
????????????a[i]?=?ILLEAGALNUM;
????????????Permutation2(a,?index+1,?n);
????????????a[i]?=?out[index];
????????}
????}
}
//修改數(shù)據(jù)數(shù)組表示其使用狀態(tài),調(diào)用深度表示取第幾個數(shù)
void?Permutation3(int?a[],?int?n)
{
????static?int?out[N];
????static?int?depth?=?-1;??????????????????????//recursive?call?depth.
????depth++;
????for(int?i?=?0;?i?<?n;?++i)
????{
????????if(depth?==?n)??????????????????????//index?>?n-1,?try?to?get?the?n-1?num,?means?it?is?ok?,?printf?it
????????{
????????????static?int?l?=?1;
????????????printf("%3d:?",?l++);
????????????for(int?k?=?0;?k<n;?k++)?printf("%d?",?out[k]);
????????????printf("?");
????????????depth--;
????????????return;
????????}
????????else?if(a[i]?!=?ILLEAGALNUM)
????????{
????????????out[depth]?=?a[i];
????????????a[i]?=?ILLEAGALNUM;
????????????Permutation3(a,?n);
????????????a[i]?=?out[depth];
????????}
????}
????depth--;
}
//額外的數(shù)組表示其使用狀態(tài),參數(shù)表示取第幾個數(shù)
void?Permutation4(int?a[],?int?index,?int?n)
{
????static?int?out[N];?//?result?array
????static?bool?m[N]?=?{1,1,1,1,1,1,1,1,1,1};?//?mark?array,?indicate?whether?the?coorespond?element?
??????????????????????????????????????????????//in?array?a?is?already?used.
????for(int?i?=?0;?i?<?n;?++i)
????{
????????if(index?==?n)??????????????????????//?if?we?already?get?the?last?num,?print?it
????????{
????????????static?int?l?=?1;
????????????printf("%3d:?",?l++);
????????????for(int?k?=?0;?k<n;?k++)?printf("%d?",?out[k]);
????????????printf("?");
????????????return;
????????}
????????else?if(true?==?m[i])??????????????????????//?if?element?i?not?used
????????{
????????????out[index]?=?a[i];
????????????m[i]?=?false;??????????????????????//?mark?element?i?as?used
????????????Permutation4(a,?index+1,?n);??????????????????????//?recursive?to?get?next?num
????????????m[i]?=?true;??????????????????????//?backdate?,?so?that?we?can?try?another?case
????????}
????}
}
雖然對于這樣的問題效率與空間相差不會特別明顯,但是我們還是來比較一下來找出最佳的一個。對于數(shù)組使用狀態(tài)的保存,顯然,用第一個方案需要動用一個額外的數(shù)組,而并沒有提高效率,所以我們應(yīng)該采用第二個方案:修改數(shù)組值的方法。對于當(dāng)前取的是哪個數(shù),如果我們用傳參數(shù)的方式,因為在排列過程中,這個遞歸函數(shù)被調(diào)用的次數(shù)是非常多的。(6個數(shù)的全排列就要調(diào)用1957次),這樣多一個參數(shù), 其每次調(diào)用壓棧出棧的消耗就顯得比較大了, 所以我們推薦用調(diào)用深度來表示。
經(jīng)過上面的討論, Permutation3就是我們的最佳選擇。?
(搬自以前blog, 2007-08-26)
轉(zhuǎn)載于:https://www.cnblogs.com/baiyanhuang/archive/2009/09/16/1730735.html
總結(jié)
以上是生活随笔為你收集整理的全排列递归实现的讨论的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ArcGIS Web 应用开发框架(AD
- 下一篇: [Flex][总结]从页面url获取参数