排列组合算法实现
来源:互联网 发布:算法工程师主要做什么 编辑:程序博客网 时间:2024/06/06 07:02
1. 递归算法
1.1 全排列:
全排列表示把集合中元素的所有按照一定的顺序排列起来,使用 P(n, n) = n!
表示n个元素全排列的个数。
例如:{1, 2, 3}的全排列为:
123;
132;
213;
231;
312;
321;
共6个,即3!=321=6。
这个是怎么算出来的呢?
首先取一个元素,例如取出了1,那么就还剩下{2, 3}。
然后再从剩下的集合中取出一个元素,例如取出2,那么还剩下{3}。
以此类推,把所有可能的情况取一遍,就是全排列了,如图:
算法分析:
1)将数组看为一个集合,将集合分为两部分:0~s和s~e,其中0~s表示已经选出来的元素,而s~e表示还没有选择的元素;
2)递归求解s-e中的全排列;
算法实现:
//ilen:数组长度//istart:数组下标,也即将集合分为两半的s// 0-------s-------e// 0 1 2 3 4 5 6 7 8 9// | |// iStart jvoid fullarray(int a[], int iLen, int iStart) ;void fullarray(int a[], int iLen, int iStart){ if (iLen == iStart) //istart 移至最后,一次全排结束 { for (int i = 0; i < iLen; ++i) { printf("%d ", a[i]); } printf("\n"); } else { //依次将j指向的元素与istart指向的元素交换,求解全排列 for(int j = iStart; j < iLen; ++j) { swapint(&a[iStart], &a[j]); //将j指向的数据与istart指向的互换 fullarray(a, iLen, iStart + 1); //求解互换后的a[s+1]-a[e]中的全排列 swapint(&a[iStart], &a[j]); //将互换的j与istart恢复,求解下一种情况 } }}
1.2 组合
组合指从n个不同元素中取出m个元素来合成的一个组,这个组内元素没有顺序。使用 C(n, k)
表示从n个元素中取出k个元素的取法数。 C(n, k) = n! / (k! * (n-k)!)
例如:从{1,2,3,4}中取出2个元素的组合为:
12;
13;
14;
23;
24;
34
方法是:先从集合中取出一个元素,例如取出1,则剩下{2,3,4}
然后从剩下的集合中取出一个元素,例如取出2
这时12就构成了一个组,如图。
算法分析:
从上面这个过程可以看出,每一次从集合中选出一个元素,然后对剩余的集合(n-1)进行一次k-1组合。
comb(set, subset, n, k){ 反向从集合中选出一个元素,将这个元素放入subset中。 调用comb(set, subset, n-1, k-1)直到只需要选一个元素为止}
算法实现:
void combine(int s[],int n,int k,void(*cbk)(int* subset,int k)){ if(k ==0) { cbk(subset, k); return ; } for(int i = n; i >= k; i--) { subset[k-1]= s[i-1]; if(k >1) { combine(s, i-1, k-1, cbk); } else { cbk(subset, subset_length); } }}
2. 非递归算法
2.1 全排列:
首先来看一段代码:
#include<iostream>#include<algorithm>usingnamespace std;int main (){ int myints[]= {1,2,3}; cout <<"The 3! possible permutations with 3 elements:\n"; sort (myints,myints+3); do { cout << myints[0]<<" "<< myints[1]<<" "<< myints[2]<< endl; } while( next_permutation (myints,myints+3)); return 0;}
这段代码是从STL Permutation
上考下来的,要注意的是第10行,首先对数组进行了排序sort()
。
第14行的 next_permutation()
是STL的函数,它的原理是这样的:生成当前列表的下一个相邻的字典序列表,里面的元素只能交换位置,数值不能改变。
什么意思呢?
123的下一个字典序是132,因为132比123大,但是又比其他的序列小。
算法分析:
(1) 从右向左,找出第一个比右边数字小的数字A。
(2) 从右向左,找出第一个比A大的数字B。
(3) 交换A和B。
(4) 将A后面的串(不包括A)反转。
算法实现: 写出next_permutation函数:
template<class T>bool next_perm(T * start, T * end){ //_asm{int 3} if(start == end) { return false; } else { T * pA = NULL,* pB; T tmp =*end;// find A. for(T * p =end; p >= start; p--) { if(*p < tmp) { pA = p; break; } else { tmp =*p; } } if(pA == NULL) { return false; // find B. for(T * p =end; p >= start; p--) { if(*p >*pA) { } pB = p; break; } } // swap A, B. tmp =*pA; *pA =*pB; *pB = tmp;// flip sequence after A for(T *p = pA+1,*q =end; p < q; p++, q--) { tmp =*p; *p =*q; *q = tmp; } return true; }}
2.2 组合:01交换法
使用0或1表示集合中的元素是否出现在选出的集合中,因此一个0/1列表即可表示选出哪些元素。
例如:[1 2 3 4 5],选出的元素是[1 2 3]那么列表就是[1 1 1 0 0]。
算法分析:
comb(set, n, k){ (1)从左到右扫描0/1列表,如果遇到“10”组合,就将它转换为”01”. (2)将上一步找出的“10”组合前面的所有1全部移到set的最左侧。 (3)重复(1)(2)直到没有“10”组合出现。}
算法实现:
template<class T>void combine(T set[],int n,int k,void(*cbk)(T set[])){ unsigned char* vec =newunsignedchar[n]; T * subset =new T[k];// build the 0-1 vector. for(int i =0; i < n; i++) { if(i < k) vec[i]=1; else vec[i]=0; } // begin scan. bool has_next =true; while(has_next) // get choosen. { int j =0; for(int i =0; i < n; i++) { if(vec[i]==1) { subset[j++]=set[i]; } } cbk(subset); has_next =false; for(int i =0; i < n -1; i++) { if(vec[i]==1&& vec[i +1]==0) { vec[i]=0; vec[i +1]=1;// move all 1 to left-most side. int count =0; for(int j =0; j < i; j++) { if(vec[j]==1) count ++; } if(count < i) { for(int j =0; j < count; j++) { vec[j]=1; } for(int j = count; j < i; j++) { vec[j]=0; } } has_next =true; break; } } } delete[] vec; delete[] subset;}
至于其中的道理,n个位置上有k个1,按照算法移动,可以保证k个1的位置不重复,且覆盖n一遍。
- C#实现排列组合算法
- 排列组合算法实现
- Java实现排列组合算法
- 【转】JAVA实现排列组合算法
- VB.NET 排列组合算法实现
- 排列组合算法的递归实现
- java排列组合算法代码实现
- java排列组合算法代码实现
- 排列组合算法的实现代码
- 排列组合算法实现--基于JS
- js实现全排列组合算法
- 高效率的排列组合算法(java实现)
- 排列组合非递归算法的实现
- 排列组合之排列问题的算法实现
- 排列组合之组合问题算法实现
- c语言实现排列组合算法问题
- c语言实现排列组合算法问题
- c语言实现排列组合算法问题
- mongodb复杂查询
- 【算法分析】QQ“一键退朝”之详细计算方法
- 碎碎念
- QT实现应用程序重启
- UIImageView 实现一组动画
- 排列组合算法实现
- 基本图算法之图的表示
- AsyncTask入门
- mysql 中查询语句表达式REGEXP用法
- 通过Wifi调试运行Android应用的IntelliJ/AndroidStudio插件:AndroidWiFiADB
- 期末总结
- SPIR-V 研究:编译器基本原理(三) - Chomsky文法分类
- 循环单向链表
- 浅读原型