本文已参与「新人创作礼」活动,一起开启掘金创作之路。
子集问题,排列问题,组合问题,细胞分裂等等,常用解法都是利用回溯算法进行解答,这里就自己问题提供一种新的解法
对于位运算不明白的也没有关系,我会用最白的话来解释什么是位运算
开始吧!
题目参考LeetCode.78子集: leetcode.cn/problems/su…
题目
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例:
输入: nums = [1,2,3]
输出: [[],[1],[2],[3],[1.2],[1,3],[2,3],[1,2,3]]
从数学方面来讲,这道题并不难,可是怎么让自己的思想在计算机上合理的展现呢 ?
在开始解题之前先了解一些基础
子集
一个数组有n个元素,那么它的子集的长度为 2……n (2**n)
位运算
let a = 1 << 2
let b = 5 << 3
// 1<<2:首先将1转为2进制,再将2进制的数字向左移2位 000000001 => 000000100 再将得到的2进制转为10进制结果输出
// 5<<3: 首先将5转为2进制,再将2进制的数字向左移3为 000000101 => 000101000 再将得到的2进制转为10进制结果输出
console.log(a); // 4
console.log(b); // 40
异或逻辑:我们使用异或时,计算机会将数字首先转换为2进制,再进行计算,最后返回10进制
let a = 7 & 4
// 首先将7转为2进制0111,再将4转为2进制,
// 0 1 1 1
// 0 1 0 0
// a = 0 1 0 0
console.log(a); // 4
我知道了子集,也知道了位运算,那么这两个有什么联系呢?
- 我们可以根据数组的长度 length,得出它的子集的长度 let len = 1 << length
- 因为 len 在2进制中,有效长度为 length+1 ,但是我们只需要遍历到 len-1 ,那么有效长度刚好为 length
- 我们可以从 0 遍历到 len - 1 , 我们可以将2进制上的每一位数字,当成数组中的每一个元素的状态, 即 1 或 0 ,1 为选中,0 为未选中。如下图
nums[1,2,3]
000 []
001 [1]
010 [2]
011 [1,2]
100 [3]
101 [1,3]
110 [2,3]
111 [1,2,3]
看完这个图不知道大家是否恍然大明白,在我们遍历的过程中,其实索引的2进制已经替我们选完了所有的子集
代码
var subsets = function (nums) {
let length = nums.length // 数组长度
let len = 1 << length // 子集长度
let result = [] // 创建空数组,收集结果
for (let i = 0; i < len; i++) {
let arr = []
for (let j = 0; j < length; j++) {
if (i & (1 << j)) { // 判断当前位置元素,是否被选中,如果选中,加入arr
arr.push(nums[j])
}
}
result.push(arr) // 将收集到数组,加入结果
}
return result
};