看到一个java的面试题,题目如下:
编写一个算法,输入任意长度的一个字符数组,返回这个数组元素的所有组合:例如输入[a,b,c],返回[[],[a],[b],[c],[a,b],[a,c],[b,c],[a,b,c]]。
当时看到这个题目的第一想法是通过递归的方法,从没个数组的长度分析然后得出结果;但是试验了很久都没有成功,如果输入四个字符的数组的话,然后返回几个只有三个字符的数组还是正确的,但是想要再得到两个字符的数组就不对了,恕我愚钝,后来在网上看到其实递归也能实现的,但是我没有深追,因为我看到一个更好的办法;所以现在给大家分享如下:
先贴代码:
public class Test {
public static void main(String[] args) {
String[] a = new String[] { "a", "b", "c", "d" };
List<String> list = Arrays.asList(a);
List<List<Object>> aa = str(list);
for(List<Object> ss:aa){
for(Object s:ss){
System.out.print(s+",");
}
System.out.println();
}
}
public static List<List<Object>> str(List<String> list) {
List<List<Object>> result = new ArrayList<List<Object>>();
long n = (long)Math.pow(2,list.size());
List<Object> combine;
for (long l=0L; l<n; l++) {
combine = new ArrayList<Object>();
for (int i=0; i<list.size(); i++) {
if ((l>>>i&1) == 1)
combine.add(list.get(i));
}
result.add(combine);
}
return result;
}
}
打印结果为:
a,
b,
a,b,
c,
a,c,
b,c,
a,b,c,
d,
a,d,
b,d,
a,b,d,
c,d,
a,c,d,
b,c,d,
a,b,c,d,
看结果,包括最上面的一个空的,刚好16个,接下来分析。
如果像我这种初学者看确实有些蒙,我也是看了很久才明白,所以在这儿记下来方便以后更快的看懂。
这里首先是得到传入字符串的所有组合的数量,即2的n次方,n是数组的长度;也就是上面说道的刚好16个组合。其实想到这一步并不难,有点数学常识的都应该知道,但是为了得到所有组合的时候就有点没有头绪了,而在这个方法中对于我们来说比较陌生但却是最关键的东西就是”>>>”这个符号了,什么意思呢?——无符号右移,而那个if中的意思就是将l无符号右移i位然后再和1去&得到的看是不是等于1;这里是二进制的右移,同样和1相与也是二进制的。其实到这里还是比较不懂为什么要把0-15这16个数要拿来分别去无符号右移i位,然后我就拿来笔记本了:
十进制 | 二进制 |
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0101 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
10 | 1010 |
11 | 1011 |
12 | 1100 |
13 | 1101 |
14 | 1110 |
15 | 1111 |
有没有一点顿悟?如果把二进制的四位看成我们传入的a,b,c,d的话,而且每位是1的话就显示,是0就不显示。这样得到的16位是不是就是所有组合了。
而且这个排列刚刚是前面得出答案的很类似,只是顺序反了而已。而刚刚那无符号右移也很简单解释了,就是去判断对应的i位上是不是1。这个方法其实看懂了过后就很容易记住而且容易理解。所以在这里分享啦;看到这儿就点一个顶吧,谢谢。