排列
1、全排列
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
我们以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符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、字典顺序输出全排列
思路:
(1)对于数组元素array[1,2...n],找出array[k]<array[k+1]的最大k值,其中0<=k<size-1,如果不存在则已经是排序的最大值
(2)对于a[k+1,k+2....n]找出一个 l,满足a[l]>a[k],且l是所有大于a[k]的最小值
(3)交换a[l]和a[k]
(4)对于a[k+1,k+2...n]的所有元素逆序
步骤(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); }
原文:http://www.cnblogs.com/luosongchao/p/3683682.html