基础模板 所有数组的子集
一、 数组子集
数组子集:
输入:nums = [1,2,3]
输出:[[],[1],[1,2],[1,2,3],[2,3],[3]]
二、位运算迭代算法
先看代码:
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> temp = new ArrayList<>();
int len = nums.length;
// 1 外部循环2的len 次方循环
for (int loop = 0; loop < (1 << len); loop++) {
temp.clear();
// 内部循环 len
for (int j = 0; j < len; j++) {
//3 位运算判断是否子集
if ((loop & (1 << j)) != 0) {
temp.add(nums[j]);
}
}
result.add(new ArrayList<>(temp));
}
return result;
}
}
2.1 外部循环次数
如果数组是是 [1,2,3,4] 同过计算 子集有16种情况
C n 0 + C n 1 + … + C n n = 2 n C_n^0+C_n^1+\ldots +C_n^n = 2^n Cn0+Cn1+…+Cnn=2n
C 4 0 + C 4 1 + … + C 4 4 = 2 4 C_4^0+C_4^1+\ldots +C_4^4 = 2^4 C40+C41+…+C44=24
2 4 2^4 24 =1>>4=16
接下来开始计算每种情况的子集
2.2 内部循环次数
内部循环容易理解,因为数组成员变量 1,2,3,4
子集只可能从成员中取出所以 j < 4
2.3 位运算
位运算是算法的关键,为什么位运算可以选择数组子集呢
假设数组长度是 4
那么外部循环是吧
**loop ** : 0,1,2,3,4,5,······15;
0000 ,0001, 0010,0011, 0100, 0101,0110 ,0111, 1000,1001,1010,1011,1100,1101,1110,1111
每次内部循环 j : 0,1,2,3
内存循环对应的二进制是: 0000 , 0001, 0010 ,0011
我们发现 对于外部每次循环
寻找一个数 sate 和 mask 做 & 运算可以计算出 j 是否属于 数组子集一个下标
sate 和j 的关系呢
1<< j =sate
1111 & sate 对应
C
4
4
C_4^4
C44
那么数组 四个成员下标全部命中
经过数学运算 得出 1<<j =sate
如果不能理解,大家可以直接记住这个 公式。
总结:位运算计算数组子集 设计很巧妙,利用二进制位运算完成子集成员的选择。
三、递归算法
略