全排列
全排列的递归实现虽然简单,但不容易理解。在绘制了一个树形结构图后,终于看清了里面的计算过程。但是虽然理清了过程,还是不明白为什么会这样设计。尤其是在递归调用的前后分别进行一次交换操作,感觉总是摸不着头绪。内心深处有个声音,往往让你绞尽脑汁不得其解的问题越是你的软肋。记得在高中学数学的时候,往往会因为一个数学难题而想破头想一个晚上,最终解出来之后总有种任通二脉打开的感觉,会感觉之前做过的类似的问题也会豁然开朗很多,虽然浪费了很长时间在这一个问题上,但收获却是无法估量的。
工作之后,很难静下心来思考一个问题很长时间,尤其是算法问题。这就是考验一个人的关键时刻。静下心来,认真思考,浮躁只会让自己的困惑越积越多,直到困惑足以打破你的自信心,你就永远都无法在这个地方抬起头来!
回头来思考这个问题,看到这个图之后清晰多了。我们看到,
(1)以a开头后面跟着(b,c,d)的排列
(2)以b开头后面跟着(a,c,d)的排列
(3)以c开头后面跟着(a,b,d)的排列
(4)以d开头后面跟着(a,b,c)的排列
当前元素与它后面的元素(包含它自己)依次进行交换
第一个swap是进行交换,而第二个swap是上一次swap的逆操作,为了防止上一次swap对之后产生影响。
template <class T> inline void Swap(T& a, T& b) { // 交换a和b T temp = a; a = b; b = temp; } template<class T> void Perm(T list[], int k, int m) { //生成list [k:m ]的所有排列方式 int i; if (k == m) { //输出一个排列方式 for (i = 0; i <= m; i++) cout << list [i]; cout << endl; } else // list[k:m]有多个排列方式 { // 递归地产生这些排列方式 for (i=k; i <= m; i++) { Swap (list[k], list[i]); Perm (list, k+1, m); Swap (list[k], list[i]); } } }
非递归算法的实现过程
//交换数组a中下标为i和j的两个元素的值 void swap(int* a,int i,int j) { a[i]^=a[j]; a[j]^=a[i]; a[i]^=a[j]; } //将数组a中的下标i到下标j之间的所有元素逆序倒置 void reverse(int a[],int i,int j) { for(;i<j;++i,--j) { swap(a,i,j); } } void print(int a[],int length) { for(int i=0;i<length;++i) cout<<a[i]<<" "; cout<<endl; } //求取全排列,打印结果 void combination(int a[],int length) { if(length<2) return; bool end=false; while(true) { print(a,length); int i,j; //找到不符合趋势的元素的下标i for(i=length-2;i>=0;--i) { if(a[i]<a[i+1]) break; else if(i==0) return; } for(j=length-1;j>i;--j) { if(a[j]>a[i]) break; } swap(a,i,j); reverse(a,i+1,length-1); } }