集合中的组合算法
Java 没有自带的求一个集合的所有子集的方法,我们可以通过集合的子集规律来求。
思路:
对集合中所有元素进行标记,0表示未选中,1表示选中。
示例:
集合{1,2,3,4},长度为4,则 0000
表示一个都不选,0001
表示选数集合中第一个元素 {1},0010
表示选中集合中第二个元素 {2},0011表示选中第一,第二元素 {1,2} …
以此类推,集合{1,2,3,4}的所有集合,包括空集,可以表示为0000
-1111
这样的二进制位码。
我们可以看出集合的所有子集的个数,是2^3=8
个。
由此,可以推理,如果我们需要输出所有的子集,只需要将每个子集用0001
这样的二进制编码表示,然后按照此二进制码输出选中的元素即可,十进制0
-15
,恰巧可以表示为二进制0000
-1111
,这样求集合的所有子集就比较简单了,所有的子集就是:0
-(2^元素数-1)
表示为二进制编码所对应的集合元素的选择。
于是,根据上面的规律,代码可以这样写,先取集合的长度,求出2^该集合的长度
是多少,比如上面的16,然后从0遍历到16-1。
遍历的时候,对0、1、2…每一个数据进行位运算,逐一判断其对应的位数,也就是二进制的表示方式,是1表示选中,0表示未选中。
实现:
package com.dodoke.test5;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
// 初始化集合
for (int i = 0; i < 4; i++) {
list.add(i + 1);
}
System.out.println(list.toString());
List<List<Integer>> all = compare1(list);
System.out.println("+++++++++++++所有子集(begin)++++++++++++++");
for (List<Integer> a : all) {
System.out.println(a.toString());
}
System.out.println("+++++++++++++所有子集(end)++++++++++++++");
int subSize = 3;
System.out.println("+++++++++++++指定子集:"+subSize+"(begin)++++++++++++++");
List<List<Integer>> subCol = compareCol(list,subSize);
for (List<Integer> a : subCol) {
System.out.println(a.toString());
}
System.out.println("+++++++++++++指定子集:"+subSize+"(end)++++++++++++++");
}
private static List<List<Integer>> compareCol(List<Integer> list, int subSize) {
List<List<Integer>> allCol = new ArrayList<>();
// 0到2^集合长度-1
for (int i = 0; i < (int) Math.pow(2, list.size()); i++) {
List<Integer> subList = new ArrayList<>();
// 重置mark值
int mark = 1;
// 该循环用于对mark移位
for (int j = 0; j < list.size(); j++) {
// 该位置是1,状态是选中,则将对于元素放到子集合中
if ((mark & i) == mark) {
subList.add(list.get(j));
}
mark <<= 1;
}
if(subList.size() == subSize) {
allCol.add(subList);
}
}
return allCol;
}
/**
* 右移索引,获取每一位
*
* @param list
* @return
*/
private static List<List<Integer>> compare(List<Integer> list) {
List<List<Integer>> all = new ArrayList<>();
// 0到2^集合长度-1
for (int i = 0; i < (int) Math.pow(2, list.size()); i++) {
List<Integer> subList = new ArrayList<>();
// 临时保存i,接下来,右移mark,得到最后一位,是否为1
int mark = i;
// 该循环用于对mark移位
for (int j = 0; j < list.size(); j++) {
if ((mark & 1) == 1) {
subList.add(list.get(j));
}
mark >>= 1;
}
all.add(subList);
}
return all;
}
/**
* 方法2:索引不动,移动mark,即左移
*
* @param list
* @return
*/
private static List<List<Integer>> compare1(List<Integer> list) {
List<List<Integer>> all = new ArrayList<>();
// 0到2^集合长度-1
for (int i = 0; i < (int) Math.pow(2, list.size()); i++) {
List<Integer> subList = new ArrayList<>();
// 重置mark值
int mark = 1;
// 该循环用于对mark移位
for (int j = 0; j < list.size(); j++) {
// 该位置是1,状态是选中,则将对于元素放到子集合中
if ((mark & i) == mark) {
subList.add(list.get(j));
}
mark <<= 1;
}
all.add(subList);
}
return all;
}
}
参考资料:https://blog.csdn.net/yongh701/article/details/53583486