集合全排列问题
设集合 R={ r1,r2,r3 … r n},显然全排列的数目为 n!。
那么如何显示集合的所有全排列呢?
可以用递归的方式实现。
解题思路
如果集合有n个元素,那么就可以看作 n-1个元素全排列之后再加上 剩下的那个元素,因为是全排列,所以每一个元素都会剩下一次。(例如:
R={1,2,3}的全排列。
若 1 剩下,则 {2,3} 先进行排列,再与1结合,结果是 1 23,1 32。
若 2 剩下,则{1, 3}排列,与2结合,结果是 2 13, 2 31。
若 3,结果为 3 12 , 321。)
继续,n-1 个数的全排列又可以看作 n-2 个数的全排列,再与剩下的那个数结合。这就与刚刚的操作重复了,一层层的往下简化,直到最后,数列中只剩下了1个元素,递归就结束了。
来看具体实现
//定义一个数组,表示集合
//参数k为起始排列数的下标,m为结束下标。
void Perm(int list[], int k, int m)
{
if(k==m) //递归结束标志,起始下标=结束下标,代表集合内就一个数了。
{
for(int i=0;i<=m;i++)
cout<<list[i]<<" "; //此时数组内的元素已经被交换好位置,一种排列可以输出。
cout<<endl;
}
else
{
for(int j=k;j<=m;j++) //变量 j从起始k开始,到m结束,表示被剩下的那个数,
{
swap(list[k],list[j]);//刚开始k=j,就相当于1=1;1被剩下,j++后,变成2,相当于2被剩下,2在数组中代替1的位置.
Perm(list,k+1,m); //除了被剩下的,其余元素排列,相当于 23排列,再继续递归
swap(list[k],list[j]); //输出一种正确排列后需要把下标再换回。不然就乱套了
}
}
}
总结
这一种解题方法是通过递归算法改变数组中元素的位置然后输出。
利用递归,将n个元素步步分解:
n变成 剩下的任意一个数(n种情况)和n-1个数。
…
直到还剩下了最后一个元素,作为递归结束的标志。
在数组中位置的交替也是难点,直接上图好理解
剩余 1 的情况是第一列,
剩余 2 的情况是第二列
…
会发现,第一列与第二列的区别就在于,1 与 2 的位置不同,则要求第二列的排列时,只需让2 代替1的位置即可,其他列也是如此。