有重复全排列

有重复全排列_递归实现

  **问题描述:**M个元素中含有相同的元素,如何得到他们的全排列(不重复排列)?
  **元素表述:**a1,a1,…a1, a2,a2,…a2,…….,an,an,…an
       其中,a1的个数为N1, a2的个数为N2,以此类推,总个数为M。
  则可以证明不重复的排列种类的数目: M!/(N1!N2!…*Nn!)
  
  解题思路与方案:
  无重复递归全排列的思想是第一个字符依次分别与后面字符(包含它本身)交换,然后除去交换后的第一个字符,剩下的字符再递归全排,全排后再交换回来至最初字符串,依次循环下去,直到第一个字符与所有字符均交换一遍。我们只需设好边界条件以结束递归,输出所有排列。
  类比,为解决本次问题,我们可以加上这样的判断:这个字符本身与本身可以相互交换,如果一个字符与后面的字符相同,那么这二个字符就不交换。如”122”,第一个字符与本身交换得到“122”,第一个字符“1”与第二个字符“2”交换得到“212”,然后考虑第一个字符“1”1与第三个字符“2”交换,此时由于第三个字符等于第二个字符,所以第一个字符不再与第三个字符交换。再考虑“212”,它的第二个字符与第三个字符交换可以得到解决“221”。此时全排列生成完毕。
  由此,我们得到了有重复全排列的核心思想:设定好边界条件以结束递归,然后从第一个字符起分别与它后面非重复出现的字符交换(包含本身的交换),再递归全排,全排后再交换回来至最初字符串,依次循环下去,直到第一个字符与所有字符都交换过。
  用编程语言描述:第i个字符与第j个字符交换时,如果[i,j)中没有与第j个数相等的字符则返回true,如果有则返回false;字符本身与本身交换返回true。

C++代码实现:

#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
void Swap(char* a,char* b);
bool IsSwap(char *pStr,int begin,int end);
void AllRange(char* pStr,int k,int m);
void Foo(char* pStr);
int main(){
    char szStr[]="baaca";
    cout<<szStr<<"的排列组合如下:\n";
    Foo(szStr);
    cin.get();
    return 0;
}
void Swap(char* a,char* b){             //交换字符
    char t=*a;
    *a=*b;
    *b=t;
}
bool IsSwap(char *pStr,int begin,int end){   //参见编程语言的实现
    for(int h=begin;h<end;h++)
        if(pStr[h]==pStr[end])
            return false;
    return true;
}
void AllRange(char* pStr,int k,int m){
    if(k==m)                                // 结束递归的边界条件
    {  
        static int s_i=1;
        cout<<"  第"<<s_i<<"个排列 "<<pStr<<endl;
        s_i++;
    }
    else
    {
        for(int i=k;i<=m;i++)
        {
            if(IsSwap(pStr,k,i))           //根据IsSwap()函数返回值来决定要不要交换字符
            {
                Swap(pStr+k,pStr+i);
                AllRange(pStr,k+1,m);      
                Swap(pStr+k,pStr+i);
            }
        }
    }
}
void Foo(char* pStr){
    AllRange(pStr,0,strlen(pStr)-1);
}

输出:

这里写图片描述

其他测试类似(只需改变szTextStr字符串),均能输出正确结果,实验成功!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值