符號的定義:
E={e1,e2,e3,······,en}表示n個元素的集合
Ei為E移去第i個元素后剩余元素的集合
perm(X)表示集合X中元素的排列方式
ei.perm(X)表示perm(X)中每個排列方式的前面均加上ei以后得到的排列方式
基本思路:
遞歸的基本部分:當n=1,即集合中只有一個元素時,只可能產生一種排列方式:perm(E)=(e)
遞歸的遞歸部分:當n>1,perm(E) = e1.perm(E1)+e2.perm(E2)+e3.perm(E3)+······+en.perm(En)
分析過程
首先定義一個數組
int E[4] = {0,1,2,3};
則對這個數組中的元素進行排列的過程為:
構造一個遞歸函數Perm(int list[],int begin,int end)
其中第一個參數表示想要進行排列的數組,第二個參數表示進行排列數組中元素的起始編號,第三個參數則表示進行排列數組中元素的結束編號
這個說起來有點繞口,舉個例子:
如果對之前所定義的數組E中的所有元素進行全排列的話,那么這個函數應該寫作Perm(E,0,3),‘0’和‘3’分別代表數組的元素的起始編號和結束編號為e0,e3,也就是對E中的四個元素都進行全排列了
可以很直觀的聯想到,當begin = end時,此時你想排列的只有一個元素,這就是遞歸的出口了
那么在全排列的過程中,函數又該如何變化?
一、當i = 0時,E0={e1,e2,e3}
這個還好說,perm(E0)即為Perm(E,1,3),可以注意到上面的begin = 0,這里的begin = 1,比上面的多了1。
二、當i = 1時,情況就有點難受了,此時E1 = {e0,e2,e3},0、2、3,從中間斷開了,這怎么搞?
解決方法如下:
我們可以將數組中元素e0和e1中的數據偷偷交換一下,用i = 0和情況來解決i = 1的情況,一開始e0 = 0,e1 = 1,交換以后就變成了e0 = 1,e1 = 0,此時E0 = {e1,e2,e3} = {0,2,3}
當然每次交換后,還要再交換回來,因為在當i = 2時,e0和e2也是需要交換的,如果不交換回來的話,實際上執行的就是e1和e2的交換了,這里是一定要注意的。
第三步和第四步,即當i = 2和i = 3的情況,省略
綜上所述,我們的代碼為:
1 #include<iostream> 2 using namespace std; 3 4 inline void Swap(int &a, int &b) 5 { 6 int temp = a; 7 a = b;; 8 b = temp; 9 } 10 11 void Perm(int list[], int begin, int end) 12 { 13 if (begin == end) 14 { 15 for (int i = 0; i <= end; i++) 16 { 17 cout << list[i]; 18 } 19 cout << endl; 20 } 21 else 22 { 23 for (int i = begin; i <= end; i++) 24 { 25 Swap(list[begin], list[i]); 26 Perm(list, begin + 1, end); 27 Swap(list[begin], list[i]); 28 } 29 } 30 } 31 32 int main() 33 { 34 int arr[3] = { 1, 2, 3 }; 35 Perm(arr, 0, 2); 36 return 0; 37 }
本文參考了:https://blog.csdn.net/xiazdong/article/details/7986015這篇博客以及《數據結構、算法與應用》
表示感謝