假設要求字串/數組的全排序,例如:"123"的全排序,第1位有3種選擇,第2位有2種選擇,第3位1種選擇,時間複雜度顯然是3!.實際上容易知道,對於n個字元,全排序一共有n!種可能,是n!時間複雜度的.我們需要找一個演算法求出所有的可能的排序,最好的辦法是遞迴,如果選用迴圈,需要n層嵌套,不太現實.假設我們已經擁有一個函數 FullPermutation(A[n])能將數組A[n]全排序,於是,我們可以將A[n]分為A[1]和A[n-1]兩個子問題,其中,A[1]可以有n種選擇,選擇辦法是:將A[1]和A[i]交換,其中,i=1,2,3,4.......記得交換後,需要複原.A[n-1]的全排序可以通過調用FullPermutation()完成.
遞推公式:
F(n): = Done n=1
= n*F(n-1) n>1
由上面的分析和遞推公式就很容易寫出代碼了:
#include<iostream>using namespace std;void Full_permutation(int* A,int beg,int end){if(beg == end){for(int i=0;i<=end;++i)cout<<A[i]<<' ';cout<<endl;return ;}for(int i=beg;i <= end; ++i)//A[1]有n種選擇,需要n次迴圈{swap(A[beg],A[i]); //選擇beg的值Full_permutation(A,beg+1,end);//求解n-1子問題swap(A[beg],A[i]);}}int main(){int A[8]={0,1,2,3,4,5,6,7};Full_permutation(A,0,7);return 0;}
擴充:由時間複雜度為(n!),我們容易聯想到n皇后問題,最經典的八皇后問題:在8×8的國際象棋上擺放八個皇后,使其不能相互攻擊,即任意兩個皇后不得處在同一行、同一列或者同一對角斜線上。我們可以這麼分析,先從行入手,第1個皇后可以在第1行上選擇8個位置,第2個皇后可以在第2行選擇7個位置(只要不和第1個皇后同列),第3個皇后可以在第1行上選擇6個位置(只要不和第1,2個皇后同列).如果不考慮對角線的攻擊方向,那麼n皇后問題可以有n!個解,和上面的全排列一樣.同樣,我們可以先考慮行和列的n!種所有情況,然後排除對角線衝突的情況,進而縮小範圍,就可以找出8皇后的解.基於這個想法,八皇后問題並不難.現在,我們還剩下最後一個問題?用什麼資料結構儲存皇后的二維座標,其實很容易,用(i,A[i])就可以儲存皇后的位置,即一個數組,索引i表示行,A[i]表示列.定義一個A[8]就可以表示八皇后.
由這個思路,代碼:
#include<iostream>using namespace std;bool isLegalQueens(int* A,int end){for(int i=0; i<=end; ++i){for(int j=i+1; j <=end; ++j){if( A[j]-A[i] == j-i || A[j]-A[i] == i-j )//兩點的斜率如果為1或-1,則為不合法return false;}}return true;}void Full_permutation(int* A,int beg,int end){if(beg == end){if(isLegalQueens(A,end)){for(int i=0;i<=end;++i)cout<<A[i]<<' ';cout<<endl;}return ;}for(int i=beg;i <= end; ++i)//A[1]有n種選擇,需要n次迴圈{swap(A[beg],A[i]);//選擇beg的值Full_permutation(A,beg+1,end);//求解n-1子問題swap(A[beg],A[i]);}}int main(){int A[8]={0,1,2,3,4,5,6,7};Full_permutation(A,0,7);return 0;}