本程序实现n选m个元素的组合,若要实现组合,可以选出每组之后用之前博文提到的全排列即可:
package edu.pku.ss.hlj;
public class Combine{
public static String[] getPortfolio(String[] numbers,int n)
{
/*
* 本程序的思路是开一个数组,其下标表示1到m个数,数组元素的值为1表示其下标
代表的数被选中,为0则没选中。
首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。
然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为
“01”组合,同时将其左边的所有“1”全部移动到数组的最左端。
当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得
到了最后一个组合。
例如求5中选3的组合:
1 1 1 0 0 //1,2,3
1 1 0 1 0 //1,2,4
1 0 1 1 0 //1,3,4
0 1 1 1 0 //2,3,4
1 1 0 0 1 //1,2,5
1 0 1 0 1 //1,3,5
0 1 1 0 1 //2,3,5
1 0 0 1 1 //1,4,5
0 1 0 1 1 //2,4,5
0 0 1 1 1 //3,4,5
*/
int m=numbers.length; //参数数组长度
int l=1; //构造数组长度标志
for(int s=m;s>m-n;s--) //获得排列数组长度L
{
l=l*s;
}
for(int z=n;z>1;z--)
{
l=l/z;
}
String[] rs = new String[l]; //返回结果
String str=""; //将第一种排列存入数组RS(即所有1都在左边的情况)
for(int x=0;x<n;x++)
{
str=str+numbers[x]+",";
}
rs[0]=str;
int[] s=new int[m]; //构造下标数组
boolean flag=true; //循环开关
int k=1; //返回结果数组长度(自增长)
for(int i=0;i<m;i++) //初始化构造下标数组
{
if(i<n)
s[i]=1;
else
s[i]=0;
}
do{
flag=false; //初始FLAG
int zerocount=0; //10转换01前的0的个数
for(int i=0;i<m-1;i++)
{
if(s[i]==0) //记录前0个数(非0即1),可以通过这个参数进行1的前移
{
zerocount++;
}
if(s[i]==1&&s[i+1]==0) //10变成01
{
s[i]=0;s[i+1]=1;
flag=true; //如果成功转换,flag设置为0,如果没有证明所以1已经移动到最后,故可以跳出DO循环
for(int j=0;j<i;j++) //10转换01前的所有1前移操作
{
if(j<i-zerocount)
s[j]=1;
else
s[j]=0;
}
String returnstr=""; //用于存储变化后的构造数组
for(int kk=0;kk<m;kk++) //通过构造数组下标,得到需要的返回串
{
if(s[kk]==1)
{
returnstr=returnstr+numbers[kk]+",";
}
}
rs[k]=returnstr; //将串存入RS数组,用于返回
i=m; //转换了第一个10后,就要跳出该次FOR循环,所以将i直接置成m
k++; //rs返回数组下标向后推一位,用于存储下个返回串
}
}
}while(flag==true);
return rs;
}
public static void main(String[] args) {
String[] strs = new String[]{"1","2","3","4","5","6"};
strs = getPortfolio(strs, 3);
for(String s : strs){
System.out.print(s+" ");
}
}
}