本文始发于个人公众号: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. docker 日志查看与清洗

    Linux下查看磁盘与目录的容量——df.du df:列出文件系统的整体磁盘使用量: du:评估文件系统的磁盘使用量(常用于评估目录所占容量) df参数: -a:列出所有的文件系统,包括系统特有的/p ...

  2. Vue 更换页面图标和title

    1.基础的做法就是直接换掉,logo换的时候需要使用icon格式的图标 title 直接在index.html 里面把原来的title注释掉 或者直接改了就行 2. 如果需要进行相应的改变啥的 ,需要 ...

  3. 阿里云ECS封25端口导致wordpress无法发送邮件的解决

    在有人评论你的文章,wordpress默认会尝试向博主发送邮件,而如果你用的是阿里云ECS,你会发现评论已经成功了,但是由于邮件发送失败会导致用户评论后页面就卡住了,原因就在于阿里云的ECS目前已经全 ...

  4. 计划任务工具-windows

    计划任务工具根据自己设定的具体时间,频率,命令等属性来规定所要执行的计划. 代码 # -*- coding: utf-8 -*- """ Module implement ...

  5. Bank2

    Account: package banking2; //账户 public class Account { private double balance;// 账户余额 public Account ...

  6. data类型的url

      所谓"data"类型的Url格式,是在RFC2397中 提出的,目的对于一些"小"的数据,可以在网页中直接嵌入,而不是从外部文件载入.例如对于img这个Ta ...

  7. Rocket - debug - TLDebugModuleInner - Program Buffer Access

    https://mp.weixin.qq.com/s/EJVqw7JPjjaib68tENl5AQ 简单介绍TLDebugModuleInner中的Program Buffer Access. 1. ...

  8. Rocket - regmapper - RegisterCrossing

    https://mp.weixin.qq.com/s/82iLT-fmDg9Comp2p9bxKg 简单介绍RegisterCrossing的实现. 1. BusyRegisterCrossing 简 ...

  9. 【大厂面试02期】Redis过期key是怎么样清理的?

    PS:本文已收录到1.1K Star数开源学习指南--<大厂面试指北>,如果想要了解更多大厂面试相关的内容,了解更多可以看 http://notfound9.github.io/inter ...

  10. 从0开始探究vue-组件化-组件之间传值

    理解 Vue中有个非常重要的核心思想,就是组件化,组件化是为了代码复用 什么是组件化 组件化,就像一个电脑主机里的主板,有内存条的插口,有硬盘,光驱等等的插口,我们的项目,就像一个电脑主机,通过各种组 ...