全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)
(一)非遞歸全排列算法
基本思想是:
1.找到所有排列中最小的一個排列P.
2.找到剛剛好比P大比其它都小的排列Q,
3.循環(huán)執(zhí)行第二步,直到找到一個最大的排列,算法結(jié)束.
下面用數(shù)學(xué)的方法描述:
給定已知序列 P = A1A2A3An ( Ai!=Aj , (1<=i<=n , 1<=j<=n, i != j ) )
找到P的一個最小排列Pmin = P1P2P3Pn 有 Pi > P(i-1) (1 < i <= n)
從Pmin開始,總是目前得到的最大的排列為輸入,得到下一個排列.
方法為:
1.從低位到高位(從后向前),找出“不符合趨勢”的數(shù)字。即找到一個Pi,使Pi < P(i+1)。
若找不到這樣的pi,說明我們已經(jīng)找到最后一個全排列,可以返回了。
2.在 P(i+1)P(i+2)Pn 中,找到一個Pj,便得 Pj"剛剛好大于"Pi.
("剛剛好大于"的意思是:在 P(i+1)P(i+2)Pn 中所有大于Pi的元素構(gòu)成的集合中最小的元素.)
3.交換 Pi , Pj 的位置.注意:此處不改變i和j的值,改變的是Pi和Pj.
4.交換后, P1P2P3Pn 并不是準(zhǔn)確的后一個排列。因為根據(jù)第1步的查找,我們有P(i+1) > P(i+2) > . > Pn
即使進(jìn)行了Pi和Pj的交換,這仍然是這一部分最大的一個排列。將此排列逆序倒置(變成最小的排列)即為所求的下一個排列.
5.重復(fù)步驟1-4,直到步驟1中找不到“不符合趨勢”的數(shù)字.
//交換數(shù)組a中下標(biāo)為i和j的兩個元素的值
void swap(int* a,int i,int j)
{
a[i]^=a[j];
a[j]^=a[i];
a[i]^=a[j];
}
//將數(shù)組a中的下標(biāo)i到下標(biāo)j之間的所有元素逆序倒置
void reverse(int a[],int i,int j)
{
for(;i<j;++i,--j)
{
swap(a,i,j);
}
}
void print(int a[],int length)
{
for(int i=0;i<length;++i)
cout<<a[i]<<" ";
cout<<endl;
}
//求取全排列,打印結(jié)果
void combination(int a[],int length)
{
if(length<2) return;
bool end=false;
while(true)
{
print(a,length);
int i,j;
//找到不符合趨勢的元素的下標(biāo)i
for(i=length-2;i>=0;--i)
{
if(a[i]<a[i+1]) break;
else if(i==0) return;
}
for(j=length-1;j>i;--j)
{
if(a[j]>a[i]) break;
}
swap(a,i,j);
reverse(a,i+1,length-1);
}
}
(二)遞歸算法
令E= {e1 , ..., en }表示n 個元素的集合,我們的目標(biāo)是生成該集合的所有排列方式。令Ei 為E中移去元素i 以后所獲得的集合,perm (X) 表示集合X 中元素的排列方式,ei . p e r m(X)表示在perm (X) 中的每個排列方式的前面均加上ei 以后所得到的排列方式。例如,如果E= {a, b, c},那么E1= {b, c},perm (E1 ) = ( b c, c b),e1 .perm (E1) = (a b c, a c b)。對于遞歸的基本部分,采用n = 1。當(dāng)只有一個元素時,只可能產(chǎn)生一種排列方式,所以perm (E) = ( e),其中e 是E 中的唯一元素。當(dāng)n > 1時,perm (E) = e1 .perm (E1 ) +e2 .p e r m(E2 ) +e3.perm (E3) + ⋯ +en .perm (En )。這種遞歸定義形式是采用n 個perm (X) 來定義perm (E), 其中每個X 包含n- 1個元素。至此,一個完整的遞歸定義所需要的基本部分和遞歸部分都已完成。
當(dāng)n= 3并且E=(a, b, c)時,按照前面的遞歸定義可得perm (E) =a.perm ( {b, c} ) +b.perm ( {a,c} ) +c.perm ( {a, b} )。同樣,按照遞歸定義有perm ( {b, c} ) =b.perm ( {c} ) +c.perm ( ), 所以a.perm ( {b, c} ) = ab.perm ( {c} ) + ac.perm ( ) = a b . c + ac.b = (a b c, a c b)。同理可得b.perm ( {a, c}) = ba.perm ( {c}) + bc.perm ( {a}) = b a . c + b c . a = (b a c, b c a),c.perm ( {a, b}) =ca.perm ( ) + cb.perm ( {a}) = c a . b + c b . a = (c a b, c b a)。所以perm (E) = (a b c, a c b, b a c, b c a,c a b, c b a)。注意a.perm ( {b, c} )實際上包含兩個排列方式:abc 和a c b,a 是它們的前綴,perm ( {b, c} )是它們的后綴。同樣地,ac.perm ( ) 表示前綴為a c、后綴為perm ( ) 的排列方式。程序1 - 1 0把上述perm (E) 的遞歸定義轉(zhuǎn)變成一個C++ 函數(shù),這段代碼輸出所有前綴為l i s t [ 0:k-1], 后綴為l i s t [ k:m] 的排列方式。調(diào)用Perm(list, 0, n-1) 將得到list[0: n-1] 的所有n! 個排列方式,在該調(diào)用中,k=0, m= n - 1,因此排列方式的前綴為空,后綴為list[0: n-1] 產(chǎn)生的所有排列方式。當(dāng)k =m 時,僅有一個后綴l i s t [ m ],因此list[0: m] 即是所要產(chǎn)生的輸出。當(dāng)k<m時,先用list[k] 與l i s t [ k:m] 中的每個元素進(jìn)行交換,然后產(chǎn)生list[k+1: m] 的所有排列方式,并用它作為list[0: k] 的后綴。S w a p是一個inline 函數(shù),它被用來交換兩個變量的值
template <class T>
inline void Swap(T& a, T& b)
{
// 交換a和b
T temp = a; a = b; b = temp;
}
template<class T>
void Perm(T list[], int k, int m)
{
//生成list [k:m ]的所有排列方式
int i;
if (k == m)
{
//輸出一個排列方式
for (i = 0; i <= m; i++)
cout << list [i];
cout << endl;
}
else // list[k:m ]有多個排列方式
{
// 遞歸地產(chǎn)生這些排列方式
for (i=k; i <= m; i++)
{
Swap (list[k], list[i]);
Perm (list, k+1, m);
Swap (list [k], list [i]);
}
}
}
上一篇:深入理解atoi()與itoa()函數(shù)的用法
欄 目:C語言
本文標(biāo)題:全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/4519.html
您可能感興趣的文章
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10深入第K大數(shù)問題以及算法概要的詳解
- 01-10深入N皇后問題的兩個最高效算法的詳解
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10深入全排列算法及其實現(xiàn)方法
- 01-10貪心算法 WOODEN STICKS 實例代碼
- 01-10C語言字符串操作總結(jié)大全(超詳細(xì))
- 01-10異步http listener 完全并發(fā)處理懲罰http懇求的小例子
- 01-10深入探討C語言中局部變量與全局變量在內(nèi)存中的存放位置
- 01-10深入分析C中不安全的sprintf與strcpy


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10C#中split用法實例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法
- 01-10使用C語言求解撲克牌的順子及n個骰子