全排列
全排列定义
一、带有不重复元素的全排列
思路
首先应该明白,全排列的总数是由一部分一部分加起来的和。这每一部分对应的是每个不同元素当作头部其他元素进行排列的总和。
图中i后的xxx泛指i打头之后的序列。
由此可以明白,我们可以把总数分为几部分,分别求出每个元素打头对应的部分的全排列数后加和即可。
求每个元素打头对应的全排列数:
举个例子,假如元素为1,2,3,并且打头的元素为1,那么对应的全排列个数就由2,3的全排列决定了,当固定1之后(就是1的位置已经固定了不再改变了),并且之后的序列由2打头,那么只要求出3的全排列就可以了,不难看出其中的递归规律。
代码实现
void Perm(char a[],int k,int m) //意为从k到m求全排列
{
if(k == m) //基准情况
{ //假如最后只剩下一个元素,也就说明确定了一个全排列
for(int i = 0 ; i <= m ; i++ )
{
cout << a[i];
}
cout << endl;
}
for(int i = k ; i <= m ; i++) //外层循环表示以每个元素打头
{
swap(a[k],a[i]);
Perm(a,k+1,m); //每进入一次Perm就说明确定了一个元素
swap(a[k],a[i]);
}
}
//为什么交换两次?
//第一次swap是用来将元素换到打头位置的
//第二次swap是将两个元素再次换回,也就是让序列保持为原状,防止重复
二、带有重复元素的全排列
带有重复元素的情况,与求全排列的代码和不重复的情况基本相同。但是带有重复元素的全排列直接带入上面求不重复元素的全排列方法会产生重复(这句话确实有点绕)。
两个问题
1.求全排列(已经解决)
2.去重
去重
首先要明白到底是哪里重复了,下面举例
也就是说两个一如果直接运算的话多数了一整轮(代指1打头所有的全排列)。
方法
当交换元素到头的时候,使用循环判断一下在这个位置之前有没有已经判断过这个元素。
int Judge(char a[],int k,int i) //判断从k到i - 1位置,有没有求过i位置对应元素的全排列
{
if(i > k)
{
for(int j = k ; j < i ; i++)
{
if(a[j] == a[i]) //相等说明已经求过了
{
return 0;
}
}
}
return 1;
}
代码实现
void Perm(char a[],int k,int m) //意为从k到m求全排列
{
if(k == m) //基准情况
{ //假如最后只剩下一个元素,也就说明确定了一个全排列
for(int i = 0 ; i <= m ; i++ )
{
cout << a[i];
}
cout << endl;
}
for(int i = k ; i <= m ; i++) //外层循环表示以每个元素打头
{
if(Judge(a,k,i))
swap(a[k],a[i]);
Perm(a,k+1,m); //每进入一次Perm就说明确定了一个元素
swap(a[k],a[i]);
}
}
int Judge(char a[],int k,int i) //判断从k到i - 1位置,有没有求过i位置对应元素的全排列
{
if(i > k)
{
for(int j = k ; j < i ; i++)
{
if(a[j] == a[i]) //相等说明已经求过了
{
return 0;
}
}
}
return 1;
}