排列与组合算法

组合算法:比如从集合m中选取n个元素,记为{n|m}
算法思路:
1. 创建一个数组,每个数组元素对应一个m中元素,该数组元素值为1,代表其对应m中的元素已被选中,否则没被选中
2. 初始化数组前面n个元素成1,代表第一种组合
3. 从左往右扫描“10”组合,并将首次发现该组合,该成“01”
4. 将该组合前面的所有1,遇到最左端
5. 回到第三步,直到找不到“10”组合为主,每次交换“10”组合就是一种可能性

例如求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
代码如下:
import java.io.*;
class test
{

public static void main (String[] args) throws java.lang.Exception
{
    int[] m={1,2,3,4,5};//集合m
    byte[] b={0,0,0,0,0};//标识数组
    for(int i=0;i<3;++i) b[i]=1;//从集合m中选取n
    print(m,b);//第一种组合情况
    boolean status=true;//ture标识找到10组合

    while(status){
        status=false;
        for(int i=0;i<b.length-1;++i){
            if(b[i]==1&&b[i+1]==0){ //找到“10”组合
                b[i]=0;b[i+1]=1;//交换成“01”组合
                int s=i-1;
                for(int j=s;j>=0;--j){//将1全部移到最左端
                    if(b[j]==0){
                        for(int k=j;k<s;++k)b[k]=1;
                        b[s]=0;--s;
                    }
                }
                status=true;
                print(m,b);
                break;
            }
        }
    }
}
//打印
public static void print(int[] m,byte[] b){
    for(byte j:b)System.out.print(j+" ");
    System.out.print("//");
    for(int i=0;i<b.length;++i)
        if(b[i]==1)System.out.print(m[i]+" ");
    System.out.println();
}

}
执行结果:
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

全排列算法:将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。
算法思路:以{1, 2, 3, 4, 5}为例

  1. 首先看最后两个数4, 5。 它们的全排列为4 5和5 4,
    即以4开头的5的全排列和以5开头的4的全排列。由于一个数的全排列就是其本身,从而得到以上结果。
  2. 再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.从而可以推断,设一组数p= {r1, r2, r3, … ,rn}, 全排列为perm(p),pn = p - {rn}。 因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), … , rnperm(pn)。当n = 1时perm(p} = r1。

为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。

代码如下:
import java.io.*;
import java.util.*;
class test
{

public static void main (String[] args) throws java.lang.Exception
{
    Queue<String> p = new LinkedList<>();//集合p
    String[] element = {"1","2","3","4","5"};//元素
    Collections.addAll(p,element);
    for(int i=1;i<element.length;++i){//将集合p中的元素,与element中的元素结合成一个新的排列,可以通过修改element.length实现,从集合中选取m个元素排序
        int size=p.size();
        for(int j=0;j<size;++j){
            String pre=p.poll();//移除上一种排列
            for(String ele:element){//如果某个元素还不存在该排列中,则组合成新的排列方式,并加入到排列集合p中
                if(pre.indexOf(ele)==-1) p.offer(pre+ele);
            }
        }
    }
    int i=0;
    for(String d:p){
        System.out.print(d+" ");
        if(++i%10==0) System.out.println();
    }
}

}

结果如下:
12345 12354 12435 12453 12534 12543 13245 13254 13425 13452
13524 13542 14235 14253 14325 14352 14523 14532 15234 15243
15324 15342 15423 15432 21345 21354 21435 21453 21534 21543
23145 23154 23415 23451 23514 23541 24135 24153 24315 24351
24513 24531 25134 25143 25314 25341 25413 25431 31245 31254
31425 31452 31524 31542 32145 32154 32415 32451 32514 32541
34125 34152 34215 34251 34512 34521 35124 35142 35214 35241
35412 35421 41235 41253 41325 41352 41523 41532 42135 42153
42315 42351 42513 42531 43125 43152 43215 43251 43512 43521
45123 45132 45213 45231 45312 45321 51234 51243 51324 51342
51423 51432 52134 52143 52314 52341 52413 52431 53124 53142
53214 53241 53412 53421 54123 54132 54213 54231 54312 54321

排列元素中含有重复元素解决方案:

  1. 添加用来标识重复次数数组
  2. 每次构建新序列时,检查该元素是否已超过规定重复元素个数,不超过,该序列为新的排序
    代码如下:
    import java.io.*;
    import java.util.*;
    class test
    {

    public static void main (String[] args) throws java.lang.Exception
    {
    Queue p = new LinkedList<>();//集合p
    String[] element = {“1”,”2”,”3”,”4”,”5”};//元素
    int[] repeat = {1,1,2,1,1};//标识元素重复数次
    Collections.addAll(p,element);
    for(int i=1;i

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值