1.不存在重复字符的排列组合
如abc,此种情况很简单,算法代码也很少,主要思想为:
- 将abc分为前部和后部,前部为a,后部abc,用a与后部每一个字符交换,如a<->a得到abc,a<->b得到bac,a<->c得到cba,最终得到abc,bac,cba三部分
- 针对上一部分中的三个后部,分别继续应用分两部,分别交换的方法,直到后部为NULL,递归输出字符串。
上述方法的递归代码如下:
#include<iostream>
using namespace std;
void swap(char *pChar1,char *pChar2)
{
char temp;
temp=*pChar1;
*pChar1=*pChar2;
*pChar2=temp;
}
void FullArry(char *cStr,char *pFirstChar)
{
char *pNode;
if(NULL==cStr)
return;
if('\0'==*pFirstChar)
{
cout<<cStr<<endl;
}
for(pNode=pFirstChar;*pNode!='\0';pNode++)
{
swap(pFirstChar,pNode);
FullArry(cStr,pFirstChar+1);
swap(pFirstChar,pNode);
}
return ;
}
int main()
{
char a[10000];
cin>>a;
FullArry(a,a);
return 0;
}
输入abc,结果为:如果输入abb
2.存在重复字母的排列组合
结果显然不符合要求,原因是因为 1.前部与后部相同的情况,我们进行了对换。2.后部如果有多个字符相同,只需要在该字符第一次出现的时候对换前后部字符。
针对上述的情况,修改代码如下:
#include<iostream>
using namespace std;
void swap(char *pChar1,char *pChar2)
{
char temp;
temp=*pChar1;
*pChar1=*pChar2;
*pChar2=temp;
}
bool CheckHasSame(char *pHead,char *pValue)
{
if('\0'==*pHead)
return true;
while(pHead!=pValue)
{
if(*pHead==*pValue)
return true;
pHead++;
}
return false;
}
void FullArry(char *cStr,char *pFirstChar)
{
char temp,*pNode;
if(NULL==cStr)
return;
if('\0'==*pFirstChar)
{
cout<<cStr<<endl;
}
pNode=pFirstChar;
if(*pNode!='\0')//此部分为前部和后部首字母相同的特殊情况,单独处理
{
swap(pFirstChar,pNode);
FullArry(cStr,pFirstChar+1);
swap(pFirstChar,pNode);
pNode++;
}
for(;*pNode!='\0';pNode++)
{
if(!CheckHasSame(pFirstChar,pNode))//保证后部待交换字符在后部首次出现
{
swap(pFirstChar,pNode);
FullArry(cStr,pFirstChar+1);
swap(pFirstChar,pNode);
}
}
return ;
}
int main()
{
char a[10000];
cin>>a;
FullArry(a,a);
return 0;
}
输入abb,结果为
3.求abc的所有组合,即a,b,c,ab,ac,bc,abc
第一种方法:
假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现。下面是这种思路的参考代码:
#include<iostream>
#include<vector>
using namespace std;
vector<char> vStr;
template<typename T>
void PrintAllMember(const vector<T> &t_toPrintOut,size_t Num)
{
for(size_t i=0;i<Num;i++)
cout<<t_toPrintOut[i];
cout<<endl;
return ;
}
void AllCombine(char *cStr,int n)
{
if(0==n)
{
PrintAllMember<char>(vStr,vStr.size());
return ;
}
if('\0'==*cStr)
return;
vStr.push_back(*cStr);
AllCombine(cStr+1,n-1);
vStr.pop_back();
AllCombine(cStr+1,n);
}
int main()
{
char a[1000];
cin>>a;
int n=strlen(a);
for(int i=1;i<=n;i++)
AllCombine(a,i);
return 0;
}
总结上述成果解决两个问题:
1,八皇后问题
题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。
代码如下:借鉴了全排列问题求解;
#include<iostream>
using namespace std;
bool RuleCheck(int arr[],int arrlen)
{
for(int i=0;i<arrlen;i++)
for(int j=i+1;j<arrlen;j++)
{
if(i-j==arr[i]-arr[j]||i-j==arr[j]-arr[i])
return false;
}
return true;
}
void Print(int arr[],int arrlen)
{
for(int i=0;i<arrlen;i++)
{
cout<<arr[i];
}
cout<<endl;
return;
}
template <typename T>
void Swap(T &t_1,T &t_2)
{
T t;
t=t_1;
t_1=t_2;
t_2=t;
}
void EigthQueen(int arr[],int arrlen,int m)
{
if(m==arrlen)
{
if(RuleCheck(arr,arrlen))
Print(arr,arrlen);
return;
}
for(int i=m;i<arrlen;i++)
{
Swap(arr[i],arr[m]);
EigthQueen(arr,arrlen,m+1);
Swap(arr[i],arr[m]);
}
return ;
}
int main(void)
{
int a[7]={0,1,2,3,4,5,6,7};
EigthQueen(a,8,0);
return 0;
}
2.输入n和m,求n到m上任意个数和等于m的情况
代码下,此题使用组合问题方法:
#include<iostream>
#include<vector>
using namespace std;
vector<int> vArr;
bool RuleCheck(vector<int> &v,int m)
{
vector<int>::iterator it;
it=v.begin();
size_t result=0;
while(it!=v.end())
{
result+=*it;
it++;
}
if(m==result)
return true;
else
return false;
}
void Print(vector<int> &v)
{
vector<int>::iterator it;
it=v.begin();
while(it!=v.end())
{
cout<<*it<<" ";
it++;
}
cout<<endl;
return;
}
void SumIsM(int headArr,int endArr,int numOfArr)
{
if(endArr-headArr<numOfArr-1)
return;
if(0==numOfArr)
{
if(RuleCheck(vArr,endArr))
Print(vArr);
return ;
}
vArr.push_back(headArr);
SumIsM(headArr+1,endArr,numOfArr-1);
vArr.pop_back();
SumIsM(headArr+1,endArr,numOfArr);
}
int main(void)
{
for(int i=1;i<=5;i++)
SumIsM(1,5,i);
return 0;
}