在这篇博文中,介绍关于1—N和整型数组的排列算法,这些算法的主要用到了递归的思想,即在函数或子过程的内部直接或者间接调用自己的算法。递归算法解决问题的特点在于:递归本身就是在子过程或者函数里调用自身;在使用递归策略时,必须有一个明确的递归结束条件,也就是不存在死递归。当然递归的缺点也是明显的,递归算法虽然间接但是算法求解的运行效率较低。同时在递归调用的过程中系统为每一层的返回点、局部变量等开辟了栈,因而递归次数过多造成栈的溢出。虽然递归算法在效率和内存等方面存在缺点,但是递归算法形式简单易于理解,因此也是必须掌握的一种编程技巧。下面将以各种排列组合算法题目,介绍递归算法的应用。
一、生成1—N的排列:输入一个整数N,然后对1,2,3......N进行全排列,输出排列的情况
#include<iostream>
using namespace std;
//记录总数
int total=0;
void printPermutation(int n,int *array,int cur)
{
int i,j;
if(cur==n)
{
total++;
for(i=0;i<n;i++)
cout<<array[i]<<" ";
cout<<endl;
}
else
{
for(i=1;i<=n;i++)
{
int flag=1;
//判断i是否已经被选过
for(j=0;j<cur;j++)
if(array[j]==i)
{
flag=0;
break;
}
//i没有被选过放入cur位置
if(flag)
{
array[cur]=i;
printPermutation(n,array,cur+1);
}
}
}
}
int main()
{
int n,array[10];
cout<<"N=";
cin>>n;
printPermutation(n,array,0);
cout<<"total="<<total<<endl;
return 0;
}
二、使用next_permutation函数对数组中的元素进行全排列
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int main()
{
int i,n,p[20];
int count=0;
cout<<"n=";
cin>>n;
for(i=0;i<n;i++)
cin>>p[i];
sort(p,p+n);
do
{
count++;
for(i=0;i<n;i++)
cout<<p[i]<<" ";
cout<<endl;
}
while(next_permutation(p,p+n));
cout<<"count="<<count<<endl;
return 0;
}
三、数组中元素的排列组合:要求数组中不能出现重复元素
#include<iostream>
#include<cstdio>
using namespace std;
int count=0;
void printPermutation(int n,int *arr,int *A,int cur)
{
int i,j;
if(cur==n)//递归边界
{
count++;
for(i=0;i<n;i++)
printf("%d ",A[i]);
printf("\n");
}
else
for(i=0;i<n;i++)//在A[i]中填各种数i
{
int ok=1;
for(j=0;j<cur;j++)
if(A[j]==arr[i])
{
ok=0;//如果i已经在A[0]~A[cur-1]出现过,则不能再选
break;
}
if(ok)
{
A[cur]=arr[i];
printPermutation(n,arr,A,cur+1);
}
}
}
int main()
{
int array[100],arr[100];
int i,n;
cout<<"n=";
cin>>n;
for(i=0;i<n;i++)
cin>>arr[i];
printPermutation(n,arr,array,0);
printf("count=%d\n",count);
return 0;
}
需要注意的是这种方法要求数组中的元素不能重复,原因在于若数组中存在重复的元素,那么当程序执行到A[j]==arr[i]时,标志变量ok的值一直为0,所以cur+1的操作将不会发生,直到外层循环for(i=0;i<n;i++)结束整个递归函数就结束了。
四、数组的排列组合:允许数组中出现重复的元素
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int total=0;
void printPermutation(int n,int *arr,int *array,int cur)
{
int i,j;
if(cur==n)
{
total++;
for(i=0;i<n;i++)
cout<<array[i]<<" ";
cout<<endl;
}
else
{
for(i=0;i<n;i++)
{
if(!i || arr[i]!=arr[i-1])
{
int c1=0,c2=0;
//arr[i]被填的个数
for(j=0;j<cur;j++)
if(array[j]==arr[i])
c1++;
//array中arr[i]的个数
for(j=0;j<n;j++)
if(arr[j]==arr[i])
c2++;
if(c1<c2)
{
array[cur]=arr[i];
printPermutation(n,arr,array,cur+1);
}
}
}
}
}
int main()
{
int array[100];
int arr[100],n,i;
cout<<"n=";
cin>>n;
for(i=0;i<n;i++)
cin>>arr[i];
sort(arr,arr+n);
printPermutation(n,arr,array,0);
printf("total=%d\n",total);
return 0;
}
注:上面数组中的元素可以重复,但是在调用递归函数之前要对该数组排序。
五、数组的排列组合:选取其中的几个数进行排列组合,允许数组中存在重复的元素
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int total=0;
void printPermutation(int m,int n,int *arr,int *array,int cur)
{
int i,j;
if(cur==m)//递归边界
{
total++;
for(i=0;i<m;i++)
printf("%d ",array[i]);
printf("\n");
}
else
for(i=0;i<n;i++)//在array中填各种数i
{
if(!i||arr[i]!=arr[i-1])
{
int c1=0,c2=0;
for(j=0;j<cur;j++)
if(array[j]==arr[i])
c1++;
for(j=0;j<n;j++)
if(arr[i]==arr[j])
c2++;
if(c1<c2)
{
array[cur]=arr[i];
printPermutation(m,n,arr,array,cur+1);
}
}
}
}
int main()
{
int array[100];
int arr[50];
int i,n;
cout<<"n=";
cin>>n;
for(i=0;i<n;i++)
cin>>arr[i];
sort(arr,arr+n);
printPermutation(3,n,arr,array,0);
printf("total=%d\n",total);
return 0;
}