递归实现全排列

来源:互联网 发布:怎样做好淘宝店 编辑:程序博客网 时间:2024/05/17 04:28

问题:对一个数组,从第n个数开始对其后面的数进行全排列;

思路:我们以1,2,3,4,5,这组数为例,令n=2,也就是对3,4,5进行全排列,而对于这三个数进行全排列,我们可以利用swap这个方法,首先考虑用第一个数3与后面的两个数分别进行交换,得到4,3,5和5,4,3;然后可以对1,2,3的后两位进行交换,得到1,3,2;对于4,3,5和5,4,3也同样交换后两位数,这样就得到了4,5,3,和5,3,4这两组数,到此我们对这三个数完成了全排列。总结一下,这里我们是从第一个数起,每个数与它后面的数分别进行交换,由此来完成全排列。

所以可以得出如下的代码:

void Perm(int* arr,int size,int n){if(n<size){for(int i=n;i<size;++i){swap(arr[n],arr[i]);Perm(arr,size,n+1);swap(arr[n],arr[i]);}}else{for(int i=0;i<size;++i){cout<<arr[i]<<" ";}cout<<endl;}}
但是这里有一个严重的问题,那就是对于一组数当中如果存在重复的元素的话,那就会出现重复情况了。

例如:对1,2,3,4,3这组数,令n=2,执行上述算法:



由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。用编程的话描述就是第i个数与第j个数交换时,要求[i,j)中没有与第j个数相等的数。

由此我们得到改进的代码:

bool isSwap(int* arr,int n,int i){for(int index=n;index<i;++index){if(arr[index]==arr[i])return false;}return true;}void Perm(int* arr,int size,int n){if(n<size){for(int i=n;i<size;++i){if(isSwap(arr,n,i)){swap(arr[n],arr[i]);Perm(arr,size,n+1);swap(arr[n],arr[i]);}}}else{for(int i=0;i<size;++i){cout<<arr[i]<<" ";}cout<<endl;}}
对1,2,3,4,3这组数,令n=2,执行上述算法:



0 0