本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是LeetCode专题第47篇文章,我们一起来看下LeetCode的第78题Subsets(子集)。

这题的官方难度是Medium,点赞3489,反对79,通过率59.9%。从这个数据我们也可以看得出来,这是一道难度不是很大,但是质量很高的题。的确,在这道题的解法当中,你会学到一种新的技巧。

废话不多说,我们先来看题意。

题意

这题的题意非常简单,和上一题有的一拼,基本上从标题就能猜到题目的意思。给定一个没有重复元素的int型数组,要求返回所有的子集,要求子集当中没有重复项,每一项当中也没有重复的元素。

样例

Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

照搬上题

刚拿到手可能有点蒙,但是稍微想一下就会发现,这一题和上题非常接近,两者唯一的不同就是,子集没有数量的限制,从空集开始,一直到它本身结束,不论多少个元素都可以。而上一题要求的是有数量限制的,也就是说上一题我们求的其实是限定了k个元素的子集。

想明白这点就简单了,显然我们可以复用上一题的算法,我们来遍历这个k,从0到n,就可以获得所有的子集了。只要你上一题做出来了,那么这题几乎没有任何难度。如果你没有看过上一题的文章的话,可以通过传送门回顾一下:

LeetCode 77,组合挑战,你能想出不用递归的解法吗?

我们直接来看代码:

class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 上一题求解k个组合的解法
def combine(n, k, ret):
window = list(range(1, k+1)) + [n+1]
j = 0 while j < k:
cur = []
for i in range(k):
cur.append(nums[window[i] - 1])
ret.append(cur[:]) j = 0
while j < k and window[j+1] == window[j] + 1:
window[j] = j + 1
j += 1
window[j] += 1 # 手动添加空集
ret = [[]]
n = len(nums)
# 遍历k从1到n
for i in range(1, n+1):
combine(n, i, ret)
return ret

二进制组合

照搬上一题的解法固然是可行的,但是这么做完全没有必要,也得不到任何收获。所以我们应该想一下新的解法。

既然这道题让我们求的是所有的子集,那么我们可以从子集的特点入手。我们之前学过,一个含有n个元素的子集的数量是。这个很容易想明白,因为n个元素,每个元素都有两个状态,选或者不选。并且这n个元素互相独立,也就是说某个元素选或者不选并不会影响其他的元素,所以我们可以知道一共会有种可能。

我们也可以从组合数入手,我们令所有子集的数量为S,那么根据上面我们用组合求解的解法,可以得到:

两者的结果是一样的,说明这个结论一定是正确的。

不知道大家看到n个元素,每个元素有两个取值有什么想法,如果做过的题目数量够多的话,应该能很快联想到二进制。因为在二进制当中,每一个二进制位就只有0和1两种取值。那么我们就可以用n位的二进制数来表示n个元素集合取舍的状态。n位二进制数的取值范围是,所以我们用一重循环去遍历它,就相当于一重循环遍历了整个集合所有的状态。

这种技巧我们也曾经在动态规划状态压缩的文章当中提到过,并且在很多题目当中都会用到。所以建议大家可以了解一下,说不定什么时候面试就用上了。

根据这个技巧, 我们来实现代码就非常简单了。

class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ret = []
n = len(nums)
# 遍历所有的状态
# 1左移n位相当于2的n次方
for s in range(1 << n):
cur = []
# 通过位运算找到每一位是0还是1
for i in range(n):
# 判断s状态在2的i次方上,也就是第i位上是0还是1
if s & (1 << i):
cur.append(nums[i])
ret.append(cur[:]) return ret

从代码来看明显比上面的解法短得多,实际上运行的速度也更快,因为我们去掉了所有多余的操作,我们遍历的每一个状态都是正确的,也不用考虑重复元素的问题。

总结

不知道大家看完文章都有一些什么感悟,可能第一种感悟就是LeetCode应该按照顺序刷吧XD。

的确如此,LeetCode出题人出题都是有套路的,往往出了一道题之后,为了提升题目数量(凑提数),都会在之前题目的基础上做变形,变成一道新题。所以如果你按照顺序刷题的话,会很明显地发现这一点。如果你从这个角度出发去思考的话,不但能理解题目之间的联系,还能揣摩出出题人的用意,这也是一件很有趣的事情。

如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

本文使用 mdnice 排版

LeetCode 78,面试常用小技巧,通过二进制获得所有子集的更多相关文章

  1. Matlab常用小技巧及部分快捷键

    Matlab常用小技巧一: 1. m文件如果是函数,保存的文件名最好与函数名一致,这点都很清楚.不过容易疏忽的是,m文件名的命名尽量不要是简单的英文单词,最好是由大小写英文/数字/下划线等组成.原因是 ...

  2. javascript的40个网页常用小技巧

    下面是javascript的40个网页常用小技巧,对网站开发人员相信会有帮助.1. oncontextmenu="window.event.returnValue=false" 将 ...

  3. HBase Shell Get 操作常用小技巧

    在工作中,有时候只是想简单看下HBase表某些关键指标的值,这个时候总不能现写Java代码去查看,以下几个小技巧你可能会经常用到. 1. 某行有许多列,只想获取指定2~3列的数据 hbase> ...

  4. 今天整理了几个在使用python进行数据分析的常用小技巧、命令。

    提高Python数据分析速度的八个小技巧 01 使用Pandas Profiling预览数据 这个神器我们在之前的文章中就详细讲过,使用Pandas Profiling可以在进行数据分析之前对数据进行 ...

  5. Scala的常用小技巧

    1."RichString.java".stripSuffix(".java") == "RichString" "http:// ...

  6. Visual Studio常用小技巧一:代码段+快捷键+插件=效率

    用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...

  7. 使用JavaScript在项目前台开发的58种常用小技巧

    oncontextmenu="return false" :禁止右键 onselectstart="return false" : 禁止选取 onpaste = ...

  8. PowerDesigner 的常用小技巧 转

    中小 订阅 修改外键命名规则 选择Database—>Edit Current DBMS选择Scripts->Objects->Reference->ConstName可以发现 ...

  9. iOS 知识-常用小技巧大杂烩

    原文链接:http://www.jianshu.com/p/7c3ee5e67d03. 自己看的. 1,打印View所有子视图 po [[self view]recursiveDescription] ...

随机推荐

  1. Spring bean工厂配置头文件

    命名 beans.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  2. Dubbo源码阅读-服务导出

    Dubbo服务导出过程始于Spring容器发布刷新事件,Dubbo在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装URL.第二部分是导出服 ...

  3. [Objective-C] 016_UI篇_UIView(上)

    在我们使用app时屏幕上能看到的UI元素(按钮,列表,图片...),我们称之为视图,都是继承与UIView,它们通常有着位置,大小,背景颜色等属性,在appl中视图和窗口展示了应用的用户界面,同时负责 ...

  4. 用pandas处理数据遇到的坑

    1.使用pandas.read_csv(filePath)方法来读取csv文件时,可能会出现这种错误:ParserError:Error tokenizing data.C error:Expecte ...

  5. 工业级CC1125模块有哪些优势?主要应用领域?

    CC1125无线模块是基于 TI 的 CC1125无线收发芯片设计,是一款完整的.体积小巧的.低功耗的无线收发模块.是 TI Chipcon 推出的 ISM 频段高性能无线收发芯片之一,最大输出功率可 ...

  6. Java实现 蓝桥杯 历届试题 小计算器

    历届试题 小计算器 时间限制:1.0s 内存限制:256.0MB 问题描述 模拟程序型计算器,依次输入指令,可能包含的指令有 1. 数字:'NUM X',X为一个只包含大写字母和数字的字符串,表示一个 ...

  7. Java实现 LeetCode 417 太平洋大西洋水流问题

    417. 太平洋大西洋水流问题 给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度."太平洋"处于大陆的左边界和上边界,而"大西洋"处于大陆的 ...

  8. Java实现 LeetCode 230 2的幂

    231. 2的幂 给定一个整数,编写一个函数来判断它是否是 2 的幂次方. 示例 1: 输入: 1 输出: true 解释: 20 = 1 示例 2: 输入: 16 输出: true 解释: 24 = ...

  9. Java实现 LeetCode 20 有效的括号

    20. 有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. ...

  10. 第六届蓝桥杯JavaC组省赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.隔行变色 隔行变色 Excel表的格子很多,为了避免把某行的数据和相邻行混淆,可以采用隔行变色的样式. 小明设计的样式为:第1行蓝色, ...