列出一个数组的全排列,数组的size是n, 那么根据高中数学排列组合知识,它的全排列个数应该是 n!
具体怎么求出来全排列,最容易想到的就是穷举法,列举出所有的可能。从递归的方式穷举,就很容易得出全排列的算法:
假设数组(a,b,c,d)的全排列可以表示为{a,b,c,d},那么{a,b,c,d} = a{b,c,d} + b{a,c,d} + c{a,b,d}+ d{a,b,c}. 类似的,{a,b,c}又表示为a{b,c} + b{a,c} +c{a,b}. 数组只有一个元素的时候全排列就是他本身。那么全排列的递归实现:
void swap(int *x, int *y){
int tmp = *x;
*x = *y;
*y = tmp;
}
void perm(int a[], int l, int r){
assert(l<=r);
if(l==r){ //出口,此时l~r区间表示的数组只有一个元素
for(int i=0; i<=r; i++){
cout<<a[i]<<" ";
}
cout<<endl;
}else{
for(int j=l; j<=r; j++){
swap(&a[j], &a[l]);
perm(a, l+1, r); //注意下一级迭代在区间 l+1~m 之间进行,不是 j+1~m
swap(&a[j], &a[l]);
}
}
}
迭代中对称使用swap(&a[j], &a[l])是为了保证递归回溯回来之后,数组的元素顺序与初始顺序一致,比如[1,2,3,4]。这样在将下一个换到首地址的元素就一定是我们期望换的元素。 例如 [1,2,3,4]这个待全排列的数组,当 j=2时,全排列是 2{1,3,4},在执行perm(a,1,3)完之后,数组顺序一定是[2,1,3,4], 再执行swap(&a[j], &a[l]) 就是[1,2,3,4], a[3]当然也一定是3; j=3时我们如愿以偿将3换到首地址。 如果不成对使用,就无法保证每次还到首地址的是我们期望的元素。
测试程序:
int main(){
int n;
cout<<"Input array size:";
cin >>n;
int array[] = (int *)malloc(sizeof(int)*n);
cout<<"Input the array elements:"<<endl;
for(int i=0; i<n; i++){
cin >> *(array+i);
}
cout<<endl<<"Input array is:"<<endl;
for(int i=0; i<n; i++){
cout<<*(array+i)<<" ";
}
cout<<endl<<"Permutation:"<<endl;
perm(array, 0, n-1);
cout<<endl;
free(array);
}
测试结果:
Input array size:4
Input the array elements:
1 2 3 4
Input array is:
1 2 3 4
Permutation:
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 3 2
1 4 2 3
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 3 1
2 4 1 3
3 2 1 4
3 2 4 1
3 1 2 4
3 1 4 2
3 4 1 2
3 4 2 1
4 2 3 1
4 2 1 3
4 3 2 1
4 3 1 2
4 1 3 2
4 1 2 3