遞迴實現全排列
阿新 • • 發佈:2020-08-06
1 求解思路
全排列表示把集合中元素的所有按照一定的順序排列起來,使用P(n, n) = n!表示n個元素全排列的個數。P(n, n)中的第一個n表示元素的個數,第二個n表示取多少個元素進行排列。
給定一個n個元素陣列,其全排列的過程可以描述如下:
- 任意取一個元素放在第一個位置,則有n種選擇;
- 再剩下的n-1個元素中再取一個元素放在第二個位置則有n-1種選擇,此時可以看做對n-1個元素進行全排列;
- 重複第二步,直到對最後一個元素進行全排列,即最後一個元素放在最後一個位置,全排列結束。
以陣列{1,2,3}為例,其全排列的過程如下:
- 1後面跟(2,3)的全排列;
- 2後面跟(1,3)的全排列;
- 3後面跟(1,2)的全排列。
圖示如下:
2 考慮陣列中有重複元素的問題
如果陣列中有重複的元素,變成了{1,2,2},那麼它的全排列就不能完全按照上面的方法求解,需要做稍微的改動。
因為全排列是將不同元素依次換到當前位置後,再對後面的元素求全排列。如果將重複的元素多次換到當前位置的話,那麼就會出現相同的排列。為了避免,我們禁止將相同的元素多次換到當前位置即可。
例如,對{1,2,2},第一個數1與第二個數2交換得到212,然後考慮第一個數1與第三個數2交換,此時由於第三個數等於第二個數,所以第一個數不再與第三個數交換。再考慮212,它的第二個數與第三個數交換可以得到解決221。此時全排列生成完畢。
這樣我們也得到了在全排列中去掉重複的規則——去重的全排列就是從第一個數字起每個數分別與它後面非重複出現的數字交換(第二次出現就不再交換,比如{1,2,2,2,2,2},與第一個2交換,不再與後面的2交換)。
#include <iostream> #include <vector> #include <stack> #include <cstring> #include <string> #include <queue> #include <algorithm> #include "TreeNode.h" #include "ListNode.h" using namespace std; // 全排列個數 int sum=0; // 列印陣列內容 void print(int array[],int len){ printf("{"); for(int i=0; i<len;++i) cout<<array[i]<<" "; printf("}\n"); } bool isSwap(int arr[], int len, int index){ for(int i = index + 1; i < len; i++){ if(arr[i] == arr[index]) return false; } return true; } // 實現兩數交換 void mySwap(int* o,int i,int j){ int tmp = o[i]; o[i] = o[j]; o[j] = tmp; } // 遞迴實現陣列全排列並列印,妙啊! void permutation(int array[],int len,int index){ // 全排列結束 if(index == len){ ++sum; print(array, len); cout<<"************************"<<endl; } else{ for(int i=index;i<len;++i){ // 首先判斷是否可以交換 if(isSwap(array, len, i)){ // 任意取一個元素放在該位置!!!!!! // 將第i個元素交換至當前index下標處 mySwap(array, index, i); // 以遞迴的方式對剩下元素進行全排列 permutation(array, len, index + 1); // 將第i個元素交換回原處,以供下一個元素與第一個元素交換 mySwap(array, index, i); } } } } int main(int argc, char* argv[]){ int array[3]={1,2,3}; permutation(array, 3, 0); cout<<"sum:"<<sum<<endl; return 0; }