排列
1、全排列
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。
void arrange(char *s,char *begin,char *end)
{
assert(s!=NULL);
if(begin==end)
{
cout<<s<<endl;
return;
}
for(char *p=begin;p<=end;p++)
{
swap(*p,*begin);
arrange(s,begin+1,end);
swap(*p,*begin);
}
}
2、去重全排列
以上的全排列方案,没有考虑重复元素的情况,例如对于字符串hee,的全排列为:hee/hee/ehe/eeh/eeh/ehe,现在要求不输出重复的排列
思路:
查看当前待排列元素,是否之前出现过,如果出现过,则不进行交换
bool repeat(char *s,char *e)
{
bool exist=false;
while(s!=e)
{
if(*s==*e)
{
exist=true;
}
s++;
}
return exist;
}
void arrange_norepeat(char *s ,char *begin,char *end)
{
assert(s!=NULL);
if(begin==end)
{
cout<<s<<endl;
return;
}
for(char *p=begin;p<=end;p++)
{
if(!repeat(begin,p))
{
swap(*p,*begin);
arrange_norepeat(s,begin+1,end);
swap(*p,*begin);
}
}
}
3、字典顺序输出全排列
思路:
找出array[k]<array[k+1]的最大k值,其中0<=k<size-1,如果不存在则已经是排序的最大值
且l是所有大于a[k]的最小值
交换a[l]和a[k]
逆序
步骤(1):
步骤(2):
步骤(3):
步骤(4):
4、非递归版本全排列
Johnson Trotter算法:
思路:
(1)初始化一个序列 1,2,3....n 并且方向全部向左,然后输出这个序列
(2)找到最大的 “可移动”元素 K,K和它所指方向的邻居交换,若不存在“可移动数”则退出
(3)所有比K大的数方向全部逆序,然后输出序列
(4)跳转到(2)
注:
“可移动”数——一个数它的箭头所指的邻居比它小,称其为“可移动”数
组合
思路:
0-1背包问题,可以将当前元素放入,也可以不放当前元素
void combination(const char *s,vector<char>& result)
{
if(s==NULL)
return;
if(*s==0)
{
for(int i=0;i<result.size();i++)
{
cout<<result[i];
}
cout<<endl;
return;
}
//放入当前元素
result.push_back(*s);
combination(s+1,result);
result.pop_back();
//不放入当前元素
combination(s+1,result);
}