集合枚举子集-学习笔记
集合枚举子集-学习笔记
算法
有一个集合,请输出它的所有子集。
子集,即为被这个这个集合包括的所有集合,包括空集。那么显然,假如有 个元素,那么有 个子集。如何枚举子集呢?
首先有一个显然的方法:用 的 dfs 枚举。但这样有弊端:时空较大,而且比较麻烦。比如一个动态规划,你每次转移都要跑一遍 dfs ,时空开销很大,而且处理不方便。那么就有另外一种方法出现:循环枚举。
首先放代码:
for(int j=st;j;j=(j-1)&st) j2=st^j;
st
就是要枚举的集合,j
就是子集,j2
就是 j
相对于 st
的补集。这里我们认为集合是一个二进制数中所有元素 ,一个 代表一个元素。这样为什么是正确的呢?
首先,为了保证枚举出来的元素都是原式的子集,我们用了 &st
这个位运算操作,因此可以保证。
其次,因为减法和按位与都是不增操作,因此可以保证不重。
那如何保证不漏呢?
模拟一遍过程:st
=1101
,我们按照循环顺序模拟过程。
我们假定 也在元素中,然后按照一定规律排列子集:
有没有发现规律?每一行,后三位都是相同的。所以这个算法的本质算是,对于每一个 ,先把这一位设定为 枚举后面所有位,再把这一位设定为 枚举一遍,然后再往前面递归。因为减 可以看做把最后一个 设定为 ,后面所有设定为 。这里建议还是自己手玩一下,找一找规律。所以这个算法类似于二进制枚举,考虑到了所有情况。
这个做法是从大到小枚举子集,着实十分优美。但要注意的是这种办法没法枚举空集,需要手动计算。
枚举子集在一些位运算的题目中可能会涉及,而这种方法可以很好地减少代码复杂度以及时空复杂度,同时也是一种很好的思想。
复杂度证明
空间不管。
时间的话,如果一个集合元素个数为 ,复杂度为 。
有一种特殊情况:枚举 到 的所有数的所有子集,此时复杂度即为:
相当于从 个位上选出 个位有值,然后有 种方案,每种方案 。
根据二项式定理:
可得:原式等价于:
等价于 。复杂度还算是比较优秀的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!