今天碰到一个打全排列的问题,仔细研究了一下,用递归的思想搞出来了,现在把大概思想贴在下面了:
假设要打印的是a, b , c , d , e , 很明显应该采用递归,递归就得有子问题
打印a, b , c , d , e 的问题可以分解成以下几个子问题
打印a, 打印{b , c , d , e}的全排列
打印b, 打印{a , c , d , e}的全排列
打印c, 打印{a , b , d , e}的全排列
打印d, 打印{a , b , c , e}的全排列
打印e, 打印{a , b , c , d } 的全排列
这样就能确定函数的输入
PrintPermutation (int a[ ], int len)
{
if (len==1) 终止操作
else
{
len个子问题,Printpermutation (a, len-1);
}
}
这里第一个要解决的问题是什么时候输出打印,如果把打印写在else里,由于递归的深度不同,就会产生排前面的数打印的次数少,排后面的数打印次数多,最保险的做法是,当len==1时,才打印整个排列,也即是写在终止操作里。
但是此时,len=1,并不确定原来一共有几个数,因此,在函数中加一个参数k, PrintPermutation (int a[], int k, int len), k是记录还有k个数要排,一直保持数组长度len的值不变是为了在打印排列的时候知道什么时候该终止。 (如果是打印字符串的全排列,可以省掉一个参数,因为字符串的结尾是确定的)
第二个问题是当长度为K时,子问题的数量是K个,如何写成一个循环的形式呢?
最简单的思路是从i=1:K,依次把把a[i]与a[1]进行交换,然后对后面的K-1个数进行全排列,用程序实现就是
swap(a[1],a[i]) PrintPermutation(a, k-1, len)
当i个子问题解决完后,还需要 swap(a[1],a[i])交换回来,再解决下一个子问题,不然,后面的次序就乱了。
理清思路写程序应该不难, swap(int &a, int &b) 这里需要加引用
void PintPermutation(int a[], int k, int len)
{
if (len<=0)
return;
if (k==1)
{
for (int i=0; i<len; i++)
cout<< a[i]<<' ';
cout<<endl;
}
else
{
for (int i=0; i<k ; i++)
{
swap(a[len-k],a[len-k+i]); % 交换
PrintPermutation(a,k-1,len);
swap(a[len-k],a[len-k+i]);
}
}
}
对于上面的程序,主要在交换这一句swap(a[len-k],a[len-k+i]);
如果把K的含义改一下,看起来更容易一点PrintPermutation(a, k, len),如果k的含义是从a[k]到a[len-1]的排列,swap就换成swap(a[k],a[k+i]), 递归调用中K+1, 递归终止条件就变成 k==len-1。
上面只是提供了一个打印全排列的思想。