您的位置:首页 > 其它

含重复元素序列的全排列

2013-12-07 01:01 246 查看
对于n个不同元素集合R的全排列问题,可以用一个简单递归的公式表达:

     当n = 1时,   P(R) = r, 否则

     P(R) = ri + P(R - {ri})       (i = 1, 2, ..., n)

P(R) : 不含重复元素的R集合的全排列

+: 表示连接

 

根据上面的公式写出递归计算出无重复元素序列的全排列:

Perm(Type list[], int k, int m) 

  if  k=m

      then  print( list )

    else

       for i=k  to  m  do

            Swap( list[k],list[i] );

            Perm(list,k+1,m);

            Swap(list[k],list[i]);

 

1. 对于含有重复元素的序列,我们不能再像上面那样简单的交换与来求解。因为,当ri 到rk 的序列中,如果存在元素 rj = rk 且 j != k 。这时,rj 必然是已经和 ri 交换过位置,并且计算了 rj + P(R{i...n} - {rj}) 。如果再将 rk 与 ri  与交换,则会得到 rk + P(R{i...n} - {rk}) 这个全排列,而rj = rk,所以,我们第二次计算多余了。

2. 再考虑当从ri  到rk 的序列中,如果不存在有元素 rj = rk , 那么,这时的情况是和普通计算时过程一样,交换ri  与rk 然后求解 就可以了,因为即使在后面存在与其他重复元素,我们也会在计算中按第1步剪掉重复计算。

所以,在求解时我们只要保证交换某一元素时,其未有在i...j之间出现过。否则交换后必会重复求解。

下面是求有重复元素的全排列的源码(我参考网上的写的):

int ok(char *origin, int x, int y)
{
int i;
if(y > x)
for(i = x; i < y; i++)
if(origin[i] == origin[y])
return 0;
return 1;
}
void permutation(char *origin, int curLen, int maxLen)
{
int i;
if(curLen >= maxLen - 1)
{
puts(origin);
fprintf(fpout, "%s\n", origin);
}
else
{
for(i = curLen; i < maxLen; i++)
if(ok(origin, curLen, i))
{
swap(&origin[i], &origin[curLen]);
permutation(origin, curLen + 1, maxLen);
swap(&origin[i], &origin[curLen]);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源码 全排列 算法