排列组合知多少--组合篇

来源:互联网 发布:win7网络图标不显示 编辑:程序博客网 时间:2024/05/22 07:44

这是我的第一篇博文,笔者资历尚浅,不对之处有劳斧正。算法之于程序,同如灵魂之于肉体,灵魂驾驭肉体,算法主宰程序,一点也不浮夸。学好算法,好比行走江湖有一技榜身;相反忽视算法,“问题”就多了,难求一解,效率低下之类,自不必说。受限于此,更是难以脱颖于同行,驻足于尖端。“码农”一说,由此得来。

切入正题,此篇主要分享3种不同的求组合算法(c++)

方法1 递归:

以一组数列为例:

1 2 3 4 5 中取3个数的组合,即n=5,r=3

组合规律如下:

首先固定第一个数为5,其后就是求解  n=4, r=2的组合数,共6个组合

其次固定第一个数为4,其后就是求解  n=3, r=2的组合数,共3个组合

最后固定第一个数为3,其后就是求解  n=2, r=2的组合数,共1个组合

这就找到了n=5,r=3与n=4,r=2,n=3,r=2,以及n=2,r=2的递归关系。

N个数中r个数组合递推到n-1,r-1;n-2,r-1;…r-1,r-1共n-r+1次递归。

递归停止条件是r=1.

#include "iostream.h"int a[100];void comb(int m,int k){int i,j;for(i=m;i>=k;i--){ a[k]=i; if(k>1)     comb(i-1,k-1); else {  for(j=a[0];j>0;j--)      cout<<a[j];  cout<<endl; } }}void main(){    intn=5,r=3;    a[0]=r;    comb(n,r); } 


方法2:非递归

通过while循环控制机制,采用回溯法的算法思想,实现非递归的组合算法。

以m=5,r=3为例。

数列0,1,2,3,4

第一个组合即为初始化的0,1,2,用数组a[100]的a[0],a[1],a[2]存储。 a[cur]标志当前控制的是a[0],a[1],a[2]中的哪一个(初始选择最后一个,即为a[2]).通过a[cur]-cur<=m-r可以判断是否越界。比如:若a[cur]为a[2]时,当a[2]取3,3-2<=5-3 故没越界,此刻组合为0,1,3;当a[2]取5时,5-2>5-3,组合输出为0,1,5很显然5根本取不到,故越界。

若不满足a[cur]-cur<=m-r ,那么执行a[--cur]++,回溯到上一层,a[cur]由a[2]变为a[1],同时a[1]=1变为a[1]=2,a[2]则变为3,组合即为0,2,3.若此时满足a[cur]-cur<m-r,表示还有数列还有空间,cur=r-1,即a[cur]由a[1]变为a[2]继续循环。不满足,则继续执行循环,下次a[cur]将回溯到a[0]。

 #include "iostream.h"int a[100];void comb(int m,int r){        int cur;                   for(int i=0;i<r;i++)        a[i]=i; cur=r-1;    do{              if (a[cur]-cur<=m-r ){                  for (int j=0;j<r;j++)             cout<<a[j] <<"  ";           cout<<endl;         a[cur]++;         continue;        }        else{            if (cur==0){             break;            }              a[--cur]++;         for(int i=1;i<r-cur;i++){              a[cur+i]=a[cur]+i;            }            if(a[cur]-cur<m-r)               cur=r-1;                         }     } while (1);}void main (){    int n;cin>>n;comb(n,i);           }


递归的方法一般可读性强,代码量较小,设计难度小,使用范围广,但占用空间大,时间复杂度大(即耗时长)。而非递归的方法一般空间,时间效率都高,但可读性差,适用范围小且设计难度大。

但在强调软件维护优先于软件效率的今天,除了少数像求阶层和斐波那契数列那样的尾递归程序,其他需要设置栈才能转换为非递归的程序就没有转换非递归的必要。(此结论参考算法设计与分析第2版)

 

方法3:for循环解决法

以n=5,r=3为例,所有组合如下:

1 2 3

1 2 4

1 2 5

1 3 4

1 3 5

1 4 5

2 3 4

2 3 5

2 4 5

3 4 5

运用我们找规律的逻辑能力,不难发现这些组合满足两个特点:1.各不相同 2.前面的数小于后面的数。所以算法设计如下:

#include “iostream.h”void main(){int n=5,i,j,k;for(i=1;i<=n;i++)for(j=1;j<=n;j++)for(k=1;k<=n;k++)if(i<j&&j<k){t=t+1;cout<<i<<” ”<<j<<””<<k<<endl;}}


此方法代码最少,但因for循环机制的局限性,不能实现n,r的通用求法。

组合问题是一个经典不衰的问题,求解方法当然不止这些,例如2进制表示法,子集数搜索法之类会在子集数专栏介绍。最后,我的博文分享就此开始,作此以助人,同时也自勉,愿中国软件开发联盟越走越远。