热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【算法LeetCode】78.子集(回溯)

78.子集-力扣(LeetCode)发布:2021年9月25日14:21:28问题描述及示例给你一个整数数组nums,数
78. 子集 - 力扣(LeetCode)

发布:2021年9月25日14:21:28

问题描述及示例


给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。


示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:
输入:nums = [0]
输出:[[],[0]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


提示:
1 <&#61; nums.length <&#61; 10
-10 <&#61; nums[i] <&#61; 10
nums 中的所有元素 互不相同


我的题解


成功前的尝试

一开始我把这道题的暴力解法想得很简单&#xff0c;就是用两层嵌套的 for 循环&#xff08;或者理解为两个指针&#xff09;不断获取 nums 中的某个区间&#xff0c;把所有区间都存入 result 即可。

/*** &#64;param {number[]} nums* &#64;return {number[][]}*/
var subsets &#61; function(nums) {let result &#61; [];result.push([]);for(let i &#61; 0; i < nums.length; i&#43;&#43;){for(let j &#61; 0; j <&#61; i; j&#43;&#43;){result.push(nums.slice(j, i&#43;1));}}return result;
};

然而这种做法是错误的&#xff1a;
在这里插入图片描述

没有考虑到子集中的元素可以为原数组中的不连续元素


错误的原因也很明显&#xff1a;没有考虑到子集中的元素不一定是 nums 中的连续的元素。

我的题解&#xff08;回溯&#xff09;

后来我想到了之前利用回溯解决的全排列的题目&#xff1a;

参考&#xff1a;【算法-LeetCode】46. 全排列&#xff08;回溯算法初体验&#xff09;_赖念安的博客-CSDN博客
参考&#xff1a;【算法-LeetCode】22. 括号生成&#xff08;回溯&#xff1b;有重复元素的全排列&#xff09;_赖念安的博客-CSDN博客

上面的【LeetCode22】就是在【LeetCode46】的基础上做了一下点改变。于是我就想本题似乎也可以用那种思想来完成。

【LeetCode46.全排列】中的递归结束条件是当 temp 的长度增长到与 nums 一样长的时候结束递归&#xff0c;且此时 temp 中存储的就是我们想要的一个结果&#xff0c;所以在结束递归的同时才能将 temp 中的结果存入 result 中。

但是本题的子集只需要所选取的元素是来自于 nums 就可以&#xff0c;不用考虑长度&#xff0c;所以不必等 temp 的长度增长到与 nums 一样长时再将 temp 中存储的元素存入 result

也就是说&#xff0c;无论 temp 的长度是多少&#xff0c;它里面存的都是一个符合题目要求的子集结果。但是&#xff0c;要注意的是&#xff0c;本题的递归结束条件仍然是 temp 的长度与 nums 一样。

这个区别就体现在把原本放到递归结束条件的 if 判断中的 result.push([...temp]) 移到了 if 判断外面&#xff1a;

// 全排列中的递归结束条件语句部分
...
if(temp.length &#61;&#61;&#61; nums.length) {result.push([...temp]);return;
}
...// 本题的递归结束条件语句部分
...
result.push([...temp]);
if(temp.length &#61;&#61;&#61; nums.length) {return;
}
...

这样就能保证每种长度的子集都能被存入 result 中。但是如果只在全排列的基础上改变这一个地方的话&#xff0c;所得的结果如下&#xff08;假设 nums&#61;[1,2,3]&#xff09;&#xff1a;

// 只改变result.push([...temp])语句的位置所得结果
[[],[1],[2],[3],[1,2],[1,3],[2,3], [2,1],[3,1],[3,2],[1,2,3], [1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

// 预期结果
[[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]

注意上面标红部分就是重复得到的结果。所以说&#xff0c;只改变上面所说的那一处的话是不能保证结果中没有重复的子集的。于是乎&#xff0c;根据之前做的那道【LeetCode22】的经验&#xff0c;我想应该还是要在生成 temp 时就对某些重复的结果进行筛选&#xff0c;也就是要对 for 循环中那个 if 判断做点手脚。这也就是所谓的 “枝剪”操作

具体需要对那个 if 判断做如下改动&#xff1a;

// 原先写法
if(used[i]) {continue;
}// 增改如下
if(used[i] || (i>0 && used.slice(0, i&#43;1).some(cur &#61;> cur))) {continue;
}

而上面的 i>0 && used.slice(0, i&#43;1).some(cur &#61;> cur) 这个判断条件就是用于判断当前遍历元素 nums[i] 之前的元素是否被使用过。

只要 nums[0] ~ nums[i-1] 其中的任意一个元素之前被用过&#xff0c;那就说明 result 中已经有了相关的子集了&#xff0c;于是就要停止往下递归&#xff0c;否则就会生成重复的子集。

其实一开始我没有用 slice(0, i&#43;1)&#xff0c;而是直接 used.some(cur &#61;> cur)&#xff0c;后来发现结果少了某些值才反应过来不应该直接对 used 中的所有元素做判断&#xff0c;而应该取前 i-1 个元素做判断。

这里利用了一个很方便的Javascript数组方法&#xff1a;Array.prototype.some()&#xff0c;相关的用法描述可看下方的MDN文档&#xff1a;

参考&#xff1a;Array.prototype.some() - Javascript | MDN

除去上面提到的两个不同点&#xff0c;其他的思路和那个【LeetCode46.全排列】的思路一模一样&#xff0c;这里就不再赘述相关的思路了。

与本题相关的其他解释请看下方注释&#xff1a;

/*** &#64;param {number[]} nums* &#64;return {number[][]}*/
var subsets &#61; function(nums) {let result &#61; [];let temp &#61; [];backtracking(nums, []);return result;function backtracking(nums, used) {result.push([...temp]);// 把上面的语句从下面的if判断中提了出来&#xff0c;这样就能保证每种长度的子集都能被捕获【tag1】if(temp.length &#61;&#61;&#61; nums.length) {return;}for(let i &#61; 0; i < nums.length; i&#43;&#43;) {// 不仅要判断当前元素是否在上一层递归中用过&#xff0c;还要判断之前的元素是否有被用过的// 只要有一个被用过&#xff0c;就不能再对其进行递归&#xff0c;【tag2】if(used[i] || (i>0 && used.slice(0, i&#43;1).some(cur &#61;> cur))) {continue;}temp.push(nums[i]);used[i] &#61; true;backtracking(nums, used);used[i] &#61; false;temp.pop(nums[i]);}}
};提交记录
10 / 10 个通过测试用例
状态&#xff1a;通过
执行用时&#xff1a;80 ms, 在所有 Javascript 提交中击败了46.93%的用户
内存消耗&#xff1a;40.3 MB, 在所有 Javascript 提交中击败了5.01%的用户
时间&#xff1a;2021/09/25 15:23

上面的程序&#xff0c;除了【tag1】和【tag2】两处&#xff0c;其他地方都和之前的全排列题目一模一样。注意对照思考。

官方题解

更新&#xff1a;2021年7月29日18:43:21

因为我考虑到著作权归属问题&#xff0c;所以【官方题解】部分我不再粘贴具体的代码了&#xff0c;可到下方的链接中查看。

更新&#xff1a;2021年9月25日15:25:24

参考&#xff1a;子集 - 子集 - 力扣&#xff08;LeetCode&#xff09;

【更新结束】

有关参考


更新&#xff1a;2021年9月25日15:25:39
参考&#xff1a;【算法-LeetCode】46. 全排列&#xff08;回溯算法初体验&#xff09;_赖念安的博客-CSDN博客
参考&#xff1a;【算法-LeetCode】22. 括号生成&#xff08;回溯&#xff1b;有重复元素的全排列&#xff09;_赖念安的博客-CSDN博客
参考&#xff1a;Array.prototype.some() - Javascript | MDN
参考&#xff1a;Array.prototype.slice() - Javascript | MDN


推荐阅读
  • 巧用arguments在Javascript的函数中有个名为arguments的类数组对象。它看起来是那么的诡异而且名不经传,但众多的Javascript库都使用着它强大的功能。所 ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • JavaScript设计模式之策略模式(Strategy Pattern)的优势及应用
    本文介绍了JavaScript设计模式之策略模式(Strategy Pattern)的定义和优势,策略模式可以避免代码中的多重判断条件,体现了开放-封闭原则。同时,策略模式的应用可以使系统的算法重复利用,避免复制粘贴。然而,策略模式也会增加策略类的数量,违反最少知识原则,需要了解各种策略类才能更好地应用于业务中。本文还以员工年终奖的计算为例,说明了策略模式的应用场景和实现方式。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • loader资源模块加载器webpack资源模块加载webpack内部(内部loader)默认只会处理javascript文件,也就是说它会把打包过程中所有遇到的 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
author-avatar
稻米屋321
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有