描述
给定一个集合求全排列
分析
- 递归
递归过程如图
求【a,b,c】的全排列
即求{ a-[b.c]; b-[a,c]; c-[a,b] } 的全排列,依次类推
void permunation(int start,int end)
{
if(end==start)//当start == end 时一次递归结束
return;
for(int i=start; i<end; i++)
{
swap(arr[start],arr[i]);//依次交换 首元素
permunation(start+1,end);//递归求子序列的全排列
swap(arr[start],arr[i]);//递归结束后 恢复数列 方便下一次递归
}
}
2、非递归实现
字典序:将集合A中的元素的排列,与某种顺序建立一一映射的关系,按照这种顺序,将集合的所有排列全部输出。这种顺序需要保证,既可以输出全部的排列,又不能重复输出某种排列,或者循环输出一部分排列。字典序就是用此种思想输出全排列的一种方式。这里以A{1,2,3,4}来说明用字典序输出全排列的方法。按照四位数的大小从小到大输出
这里给出算法。
1、对集合A排序
2、a[1,n]
中从你n~1查找元素 a[k] 满足a[k]>a[k-1]
的元素,如果不存在k说明没有下一个排序。
3、找到a[k+1,n]
之间比a[k]
大的最小的元素 a[i]
交换 swap(a[i],a[k]);
4、a[k+1,n]
为从大到小排列 逆序a[k+1,n]
求得下一个排列。
使用字典序输出集合的全排列需要注意,因为字典序涉及两个排列之间的比较,对于元素集合不方便比较的情况,可以将它们在数组中的索引作为元素,按照字典序生成索引的全排列,然后按照索引输出对应集合元素的排列,对于集合A{a,b,c,d},可以对其索引1234进行全排列生成。这么做还有一个好处,就是对于字典序全排列生成算法,需要从字典序最小的排列开始才能够生成集合的所有排列,如果原始集合A中的元素不是有序的情况,字典序法将无法得到所有的排列结果,需要对原集合排序之后再执行生成算法,生成索引的全排列,避免了对原始集合的排序操作。
字典序算法还有一个优点,就是不受重复元素的影响。例如1224,交换中间的两个2,实际上得到的还是同一个排列,而字典序则是严格按照排列元素的大小关系来生成的。
void reverseArr(arr,start,end)//翻转 [start,end]
{
for(int i=start; i<end; i++)
for(int j=end; j!=j; j--)
std::swap(arr[i],arr[j])
}
void reverseArr_2(arr,star,end)//[start,end]
{
for(int i=0; i*2<end-start,i++)
swap(arr[start+i],start[end-i]);
}
void generatePermunation(int *arr,int len)
{
std::sort(arr,arr+len);
while(true)
{
int k=-1;
for(int i=len-1; i!=0; i--)
{
if(a[i]>a[i-1])
k = i-1;
}
if(k<0)
return ;
for(int j=len-1;j!=0; j--)
{
if(a[j]>a[k])
{
swap(a[j],a[k]);
reverse(a[k+1],a[len-1]);
//输出arr
}
}
}
}