LeetCode 78,面试常用小技巧,通过二进制获得所有子集
本文始发于个人公众号: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,面试常用小技巧,通过二进制获得所有子集的更多相关文章
- Matlab常用小技巧及部分快捷键
Matlab常用小技巧一: 1. m文件如果是函数,保存的文件名最好与函数名一致,这点都很清楚.不过容易疏忽的是,m文件名的命名尽量不要是简单的英文单词,最好是由大小写英文/数字/下划线等组成.原因是 ...
- javascript的40个网页常用小技巧
下面是javascript的40个网页常用小技巧,对网站开发人员相信会有帮助.1. oncontextmenu="window.event.returnValue=false" 将 ...
- HBase Shell Get 操作常用小技巧
在工作中,有时候只是想简单看下HBase表某些关键指标的值,这个时候总不能现写Java代码去查看,以下几个小技巧你可能会经常用到. 1. 某行有许多列,只想获取指定2~3列的数据 hbase> ...
- 今天整理了几个在使用python进行数据分析的常用小技巧、命令。
提高Python数据分析速度的八个小技巧 01 使用Pandas Profiling预览数据 这个神器我们在之前的文章中就详细讲过,使用Pandas Profiling可以在进行数据分析之前对数据进行 ...
- Scala的常用小技巧
1."RichString.java".stripSuffix(".java") == "RichString" "http:// ...
- Visual Studio常用小技巧一:代码段+快捷键+插件=效率
用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...
- 使用JavaScript在项目前台开发的58种常用小技巧
oncontextmenu="return false" :禁止右键 onselectstart="return false" : 禁止选取 onpaste = ...
- PowerDesigner 的常用小技巧 转
中小 订阅 修改外键命名规则 选择Database—>Edit Current DBMS选择Scripts->Objects->Reference->ConstName可以发现 ...
- iOS 知识-常用小技巧大杂烩
原文链接:http://www.jianshu.com/p/7c3ee5e67d03. 自己看的. 1,打印View所有子视图 po [[self view]recursiveDescription] ...
随机推荐
- MyBatis通过注解方式批量添加、修改、删除
唯能极于情,故能极于剑 注: 本文转载于:CodeCow · 程序牛 的个人博客:http://www.codecow.cn/ 一.数据库实体DO public class User implemen ...
- SpringBoot整合SpringSecurity实现JWT认证
目录 前言 目录 1.创建SpringBoot工程 2.导入SpringSecurity与JWT的相关依赖 3.定义SpringSecurity需要的基础处理类 4. 构建JWT token工具类 5 ...
- [Firefox附加组件]0002.添加菜单项
Add-onSDK 还不能为火狐浏览器提供一个API添加新的菜单项.但它是可扩展的设计,所以任何人都可以建立和发布模块,使用插件开发者.大牛埃里克沃尔德写的MenuItems模块,能够使我们很方便的添 ...
- Wilson's theorem在RSA题中运用
引言 最近一段时间在再练习数论相关的密码学题目,自己之前对于数论掌握不是很熟练,借此机会先对数论基本的四大定理进行练习 这次的练习时基于Wilson's theorem(威尔逊定理)在RSA题目中的练 ...
- 设计Person类 代码参考
#include <iostream> using namespace std; class Trapezium { private: int x1,y1,x2,y2,x3,y3,x4,y ...
- Vue中导出Excel表格方法
本文记录一下在Vue中实现导出Excel表格的做法.参考度娘上各篇博客,最后实现功能 Excel表格,我的后端返回的是数据流,然后文件名是放进了content-disposition中,前端进行获取. ...
- Java实现 LeetCode 477 汉明距离总和
477. 汉明距离总和 两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量. 计算一个数组中,任意两个数之间汉明距离的总和. 示例: 输入: 4, 14, 2 输出: 6 解释: 在二进 ...
- Java实现 LeetCode 84 柱状图中最大得矩形
84. 柱状图中最大的矩形 给定 n 个非负整数,用来表示柱状图中各个柱子的高度.每个柱子彼此相邻,且宽度为 1 . 求在该柱状图中,能够勾勒出来的矩形的最大面积. 以上是柱状图的示例,其中每个柱子的 ...
- Java实现 LeetCode 65 有效数字
65. 有效数字 验证给定的字符串是否可以解释为十进制数字. 例如: "0" => true " 0.1 " => true "abc&q ...
- java实现Floyd算法
1 问题描述 何为Floyd算法? Floyd算法功能:给定一个加权连通图,求取从每一个顶点到其它所有顶点之间的最短距离.(PS:其实现功能也称完全最短路径问题) Floyd算法思想:将顶点i到j的直 ...