写点什么

LeetCode 题解:90. 子集 II,迭代 + 位运算,JavaScript,详细注释

用户头像
Lee Chen
关注
发布于: 2020 年 11 月 04 日
LeetCode题解:90. 子集 II,迭代+位运算,JavaScript,详细注释

原题链接:https://leetcode-cn.com/problems/subsets-ii/


解题思路:


  1. 用两遍循环暴力穷尽所有集合。

  2. 第一遍循环是for (let i = 0; i < 1 << nums.length; i++) {},即穷尽了所有子集数量,但不做是否存值的判断。

  3. 第二遍循环for (let j = 0; j < nums.length; j++) {},即遍历每个索引,在通过i & (1 << j)判断当前索引是否需要存值。

  4. 关于去重的方法,可以参考题解详细通俗的思路分析,多解法中的位运算部分,结合我的注释加深理解。


/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsetsWithDup = function (nums) {
let result = []; // 存储最终结果
nums.sort((a, b) => a - b); // 将nums排序,保证能够有效去重
// 第一遍循环
// 以nums为[1 ,2 ,3]为例,1 << nums.length生成的为二进制数1000
// 该循环遍历了所有可能的子级
for (let i = 0; i < 1 << nums.length; i++) {
let subset = []; // 存储当前可能的子级
// 判断是否可以存储当前的子集,为true时可以存储。
// 为false时,表示当前找到了重复子集,不可以存储
let judge = true;
// 第二遍循环
// 利用该循环,遍历出每个索引对应的二进制数
for (let j = 0; j < nums.length; j++) {
// 1 << j生成的是二进制数1、10、100,分别对应3个索引
// 每个i都与1 << j进行与操作。即完成了当前输赢是否要存入值的判断
// 也就是每个索引都有存或不存值两种该状态
if (i & (1 << j)) {
/* console.log(
i.toString(2).split('').reverse().join(''),
(1 << j).toString(2).split('').reverse().join(''),
(1 << (j - 1)).toString(2).split('').reverse().join(''),
// nums[j] === nums[j - 1],
// (i & (1 << (j - 1))) === 0,
Boolean(nums[j] === nums[j - 1] && (i & (1 << (j - 1))) === 0),
[...subset, nums[j]],
); */
// 判断当前子集是否已被存储过
if (
// 表示相邻的两个值相等,如果相等,则表示可能出现重复值
nums[j] === nums[j - 1] &&
// 判断j-1的值是否已被存储过,如果为否,则表示可以跳过
// 达到了只保存第一次nums[j]和nums[j - 1]都存为子集的效果,其余的子集都会被跳过
(i & (1 << (j - 1))) === 0
) {
// 将judge设置为false,表示当前子集不需要被保存
judge = false;
// 由于当前子集不需要被保存,可以直接退出循环
// 实际上不退出对最终结果没有影响
break;
} else {
// 将需要存储的值,存入当前集合中
subset.push(nums[j]);
}
}
}
// console.log('finished subset', judge, subset);
// 将当前集合存入结果
if (judge) {
result.push(subset);
}
}
return result;
};
复制代码


发布于: 2020 年 11 月 04 日阅读数: 33
用户头像

Lee Chen

关注

还未添加个人签名 2018.08.29 加入

还未添加个人简介

评论

发布
暂无评论
LeetCode题解:90. 子集 II,迭代+位运算,JavaScript,详细注释