【Java与排列组合】有五个球,其中2个一样的黑球,红白蓝球各一个,现从中取出4个球排成一列,求所有不同的排法

【数学思路】

该问题分两类

一类是取出红蓝白黑四色球,其排法是A_4_4=24种

一类是取出两个黑球加两个其它颜色的球,两黑球和两色球排列是一个可重复元素的全排列问题,其排法是A_4_4/A_2_2/A_1_1/A_1_1=24/2=12种,从红蓝白中选出两色球是C_3_2=3种,故总数是12*3=36种。

把两类加起来,总数是60种。

【程序思路】

程序思路比较简单,进行A_5_4的排列,用TreeSet清除重复并排序就OK了。

【代码】

辅助类Arranger:

import java.util.ArrayList;
import java.util.List;

/**
 * 用于产生排列结果的工具类
 * 从n个元素中取出m个元素,按照一定的顺序排成一列。得到所有排列的方案
 */
class Arranger {
    // 保存在内部的对原始元素数组的引用
    private int[] arr;

    // 总计多少元素,此即数组长度
    private final int n;

    // 选多少个
    private final int m;

    // 返回结果
    private List<List<Integer>> results;

    /**
     * 构造函数一
     * 这个构造函数是用于全排列的(n=m=数组长度)
     *
     * @arr 原始元素数组(注意不要有重复项)
     */
    public Arranger(int[] arr) {
        this.arr = arr;
        this.n = arr.length;
        this.m = arr.length;

        this.results = new ArrayList<>();
        doArrange(new ArrayList<>());
    }

    /**
     * 构造函数二
     * 这个构造函数是用于部分排列的(m<n=数组长度)
     *
     * @param arr    原始元素数组 (注意不要有重复项)
     * @param selCnt 选多少个
     */
    public Arranger(int[] arr, int selCnt) {
        this.arr = arr;
        this.n = arr.length;
        this.m = selCnt;
        if (m > n) {
            throw new ArrayIndexOutOfBoundsException("m:" + m + " >n:" + n);
        }

        this.results = new ArrayList<>();
        doArrange(new ArrayList<>());
    }

    /**
     * 使用递归进行全排列,结果放在results中
     *
     * @param initialList 初始链表
     */
    private void doArrange(List<Integer> initialList) {
        List<Integer> innerList = new ArrayList<>(initialList);

        if (m == initialList.size()) {
            results.add(innerList);
        }

        for (int i = 0; i < arr.length; i++) {
            if (innerList.contains(arr[i])) {
                continue;
            }

            innerList.add(arr[i]);
            doArrange(innerList);
            innerList.remove(innerList.size() - 1);
        }
    }

    /**
     * 获得结果链表的引用
     *
     * @return
     */
    public List<List<Integer>> getResults() {
        return results;
    }

    // 测试
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4};
        Arranger arranger = new Arranger(numbers);

        System.out.println("四元素全排列示例:");
        int idx = 0;
        for (List<Integer> re : arranger.getResults()) {
            System.out.println(String.format("%02d", ++idx) + "." + re);
        }

        /*Arranger arranger2 = new Arranger(numbers, 2);
        System.out.println("\n四选二排列示例:");
        idx = 0;
        for (List<Integer> re : arranger2.getResults()) {
            System.out.println(String.format("%02d", ++idx) + "." + re);
        }*/
    }
}

主类FiveBallTwoBlack:

import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * 有五个球,其中2个一样的黑球,红白蓝球各一个
 * 现从中取出4个球排成一列,求所有不同的排法
 * @author 逆火
 *
 */
public class FiveBallTwoBlack {
    public static void main(String[] args) {
        final String[] balls= {"黑","黑","红","白","蓝"};
        int[] numbers = {0, 1, 2, 3, 4};
        Arranger arranger = new Arranger(numbers,4);
        
        Set<String> set=new TreeSet<String>();
        for (List<Integer> line : arranger.getResults()) {
            String sentence="";
            
            for(int i:line) {
                sentence+=balls[i];
            }
            
            set.add(sentence);
        }
        
        int idx = 0;
        for(String s:set) {
            System.out.println(String.format("%02d.", ++idx)+s);
        }
    }
}

【输出】

01.白红蓝黑
02.白红黑蓝
03.白红黑黑
04.白蓝红黑
05.白蓝黑红
06.白蓝黑黑
07.白黑红蓝
08.白黑红黑
09.白黑蓝红
10.白黑蓝黑
11.白黑黑红
12.白黑黑蓝
13.红白蓝黑
14.红白黑蓝
15.红白黑黑
16.红蓝白黑
17.红蓝黑白
18.红蓝黑黑
19.红黑白蓝
20.红黑白黑
21.红黑蓝白
22.红黑蓝黑
23.红黑黑白
24.红黑黑蓝
25.蓝白红黑
26.蓝白黑红
27.蓝白黑黑
28.蓝红白黑
29.蓝红黑白
30.蓝红黑黑
31.蓝黑白红
32.蓝黑白黑
33.蓝黑红白
34.蓝黑红黑
35.蓝黑黑白
36.蓝黑黑红
37.黑白红蓝
38.黑白红黑
39.黑白蓝红
40.黑白蓝黑
41.黑白黑红
42.黑白黑蓝
43.黑红白蓝
44.黑红白黑
45.黑红蓝白
46.黑红蓝黑
47.黑红黑白
48.黑红黑蓝
49.黑蓝白红
50.黑蓝白黑
51.黑蓝红白
52.黑蓝红黑
53.黑蓝黑白
54.黑蓝黑红
55.黑黑白红
56.黑黑白蓝
57.黑黑红白
58.黑黑红蓝
59.黑黑蓝白
60.黑黑蓝红

【结论】

数学和程序的结果可以相互印证。

END

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值