4.算法-所有数组的子集

一、 数组子集

数组子集:
输入: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
如果不能理解,大家可以直接记住这个 公式。

总结:位运算计算数组子集 设计很巧妙,利用二进制位运算完成子集成员的选择。

三、递归算法

猜你喜欢

转载自blog.csdn.net/keep_learn/article/details/115366029