全排列和全组合问题

排列问题

求这4个球的所有排列方式

暴力枚举方式。

public static void main(String[] args) {
    	char[] cc = {'A','B','C','D'};
    	int count = 0;
    	
    	for(int i=0;i<4;i++){
    		for(int j=0;j<4;j++){
    			if(i==j)continue;
    			for(int k=0;k<4;k++){
    				if(k==i||k==j)continue;
    				for(int m=0;m<4;m++){
    					if(m==k||m==j||m==i)continue;
    					System.out.println(cc[i]+" "+cc[j]+" "+cc[k]+" "+cc[m]);
    					count++;
    				}
    			}
    		}
    	}
    	System.out.println(count);
    	
    }

分析这4层循环。

分别枚举了4个球在于其他球不重复情况下的情况

i 的 变化范围是  0 - 4 

j 的 变化范围是  0 - 4   -1(不能和i重复)

k的 变化范围是  0 - 4   -2 (不能和i重复也不能和j重复)

m的 变化范围的 0 - 4  - 3   (不能和i重复也不能和j重复也不能也k重复)

结论4个数的全排列个数是 4 * 3 * 2 *1   

求4个球 中取2个球的不同排列

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
    	char[] cc = {'A','B','C','D'};
    	int count = 0;
    	
    	for(int i=0;i<4;i++){
    		for(int j=0;j<4;j++){
    			if(i==j)continue;
    			System.out.println(cc[i]+" "+cc[j]);
    			count++;
    		}
    	}
    	
    	System.out.println(count);
    	
    }  
}


分析这2层循环。

既然是从4个球中取2个球的全排列,那么2层循环模拟2个球分别是什么

i 的 变化范围是  0 - 4 

j 的 变化范围是  0 - 4   -1(不能和i重复)

结论4个球 中取 2个的全排列个数是 4 * 3  


看看这2层循环和上面4层循环的区别


此时已经可以完全解释的通百度上的那个全排列公式

n种颜色取m种的全排列  的种数


n的阶乘  除以    (n-m)的阶乘      :   


原理 n 的阶乘的 n种 全排列一定是 n的阶乘

要取 其中 m的 全排列,   

像之前的循环一样,原本是n重循环,此时应该 将多余层数循环(n-m)除以


全排列经典递归模拟:

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
    	char[] cc = {'A','B','C','D'};
    	all(cc,0);
    }
    public static void all(char[] cc,int index){
    	if(index==cc.length){
    		System.out.println(Arrays.toString(cc));
    		return;
    	}
    	for(int i=index;i<cc.length;i++){
    		char temp = cc[i];cc[i]=cc[index];cc[index] = temp;
    		all(cc,index+1);
    		temp = cc[i];cc[i]=cc[index];cc[index] = temp;
    	}
    }
}


组合问题

从球中任意取2个的取法。

(这个组合问题也是刚刚学会,强行解释一番)

组合和排列的区别:

排列会存在

红 橙、橙红

但是 组合 不会出现重复

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
    	char[] cc = {'A','B','C','D'};
    	int count = 0;
    	for(int i=0;i<4;i++){
    		for(int j=i+1;j<4;j++){
    			System.out.println(cc[i]+" "+cc[j]);
    			count++;
    		}
    	}
    	System.out.println(count);
    }
}



对于,这个两层循环,可以思考 4 个球 中取 2 个球的全排列   4*3 = 12 

用 12 除以,2的阶乘     2就是取几个球。 过滤掉 排列中重复的那些球,就可以得到组合的所有种类

总结,n个球中取m个球,的组合数就是 他的所有排列种类  除以    m的阶乘

这样去理解公式


用递归去求,m个球中取n个数的全组合数

public static int f(int m,int n){
    	if(m==n)return 1;
    	if(n==0)return 1;
    	return f(m-1,n-1) + f(m-1,n);
    }

模拟组合递归法

import java.util.Arrays;

public class Main {
	
   //不重复的组合问题
    public static void main(String[] args) {
    	char[] cc = {'A','B','C','D'};
    	
    	//枚举法
    	for(int i=0;i<4;i++){
    		for(int j=i+1;j<4;j++){
    			for(int k=j+1;k<4;k++){
    	    		System.out.println(cc[i]+""+cc[j]+""+cc[k]);
    	    	}
        	}
    	}
    	System.out.println();
    	
    	g1(cc,new char[3],0,0);
    	System.out.println();
    	g2(cc,"",0,3);
    }

    //递归1
    //cc原球种类
    //ss已经取好的球
    //index 应该从第几个球开始取
    //n 现在应该取第几个球
    public static void g1(char[] cc,char[] ss,int index,int n){
    	if(n==ss.length){
    		System.out.println(Arrays.toString(ss));
    		return;
    	}
    	for(int i=index;i<cc.length;i++){
    		ss[n] = cc[i];
    		g1(cc,ss,i+1,n+1);//加强注意,i+1 不是index+1
    	}
    }
    
    //递归2
    //cc原球种类
    //ss已经取好的球
    //index 应该从第几个球开始取
    //n 一共打算取几个球
    //使用str的长度判断球取够了没
    public static void g2(char[] cc,String str,int index,int n){
    	if(str.length()==n){
    		System.out.println(str);
    		return;
    	}
    	for(int i=index;i<cc.length;i++){
    		g2(cc,str+cc[i],i+1,n);
    	}
    }

}


组合重复

X星球要派出一个5人组成的观察团前往W星。其中:A国最多可以派出4人。B国最多可以派出2人。C国最多可以派出2人。D国最多可以派出1人。E国最多可以派出1人。F国最多可以派出3人。

那么最终派往W星的观察团会有多少种国别的不同组合呢?

模拟该组合

这里我分别使用了数组做形参,和字符串做形参两种方式进行模拟。

字符串做形参的好处是,字符串是一个常量,每次递归压栈都会创建新的字符串,所以不需要huis

import java.util.Arrays;  
  
public class Main {  
    static char[] cc = { 'A', 'B', 'C', 'D', 'E', 'F' };  
    static int count = 0;  
  
    public static void main(String[] args) {  
        int[] data = { 4, 2, 2, 1, 1, 3 };// 每个元素的最大个数  
        int[] x = new int[6]; // 每个元素取几个  
  
//         f(data,x,0,3);//取三个,  
  
        f2(data, 0, 3, "");  
        System.out.println(count);  
  
    }  
  
    // k代表 现在正在取 那个元素  
    public static void f(int[] data, int[] x, int k, int goal) {  
        if (k == data.length) {  
            if (goal == 0) {  
                for (int i = 0; i < x.length; i++) {  
                    for (int j = 0; j < x[i]; j++) {  
                        System.out.print(cc[i]);  
                    }  
                }  
                System.out.println();  
                count++;  
            }  
            return;  
  
        }  
  
        // data[k]和goal中的最小值就是说,我还能最多取几个 x[k]=i;//这个位置取几个; //再从下一个位置开始取  
        for (int i = 0; i <= Math.min(goal, data[k]); i++) {  
        	//这个国家出几个人。
        	x[k]=i;
        	
        	//进入递归
            f(data, x, k + 1, goal - i);  
        }  
        // 模拟完这个位置回溯一下  
        x[k] = 0;  
    }  
  
    // 使用一个字符串做该次测试的变化条件  
    public static void f2(int[] data, int k, int n, String str) {  
        if (k == data.length) {  
            if (n == 0) {  
                System.out.println(str);  
                count++;  
  
            }  
            return;  
  
        }  
        String s2 = str;  
        for (int i = 0; i <= Math.min(data[k], n); i++) {  
            f2(data, k + 1, n - i, s2);  
            s2 += cc[k];  
        }  
    }  
}  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值