集合的二进制表示法
集合的二进制表示法
概述
有一个包含个不同元素的集合,假设为:,他的子集个数有个,当的大小不是特别大的时候,例如时(约等于),我们则可以在合理的时间内枚举出它的所有子集。
子集的表示方法
-
数组表示法
- 直接用数组表示集合,这种表示方式比较直接,c++中的任何容器类型都可以用来存储集合
-
01 bit串表示法(二进制表示法)
- 使用一个整数的二进制形式来表示集合,二进制数第i个bit位为0,则表示全集中的第i个元素不在子集中,二进制数第i个bit位为1,则表示在子集中;例如一个包含3个元素的集合{1,2,3}的所有子集,可以用<8的整数来表示:
- 0 -> 000 -> {}
- 1 -> 001 -> {1}
- 2 -> 010 -> {2}
- 3 -> 011 -> {1,2}
- 4 -> 100 -> {3}
- 5 -> 101 -> {1,3}
- 6 -> 110 -> {2,3}
- 7 -> 111 -> {1,2,3}
子集的遍历
-
递归遍历
使用数组表示法表示集合时,子集遍历使用递归遍历实现起来会比较容易些,二进制表示法当然也可以使用递归遍历,但是二进制表示法有更简洁的遍历方法,没有必要使用递归实现
- 设递归函数enumerate(vector
s, int idx),s中存储下标小于idx的元素中选出的子集,初始为空,当遍历到第idx个元素时,有两种选择,一种是选择第idx个元素,加入到s中,一种是不选择idx元素,则s保持不变 -
代码实现 —— 数组表示法
1 | void print(const vector<int>& s) { |
-
迭代遍历
-
代码实现 —— 数组表示法
遍历过程相当于二进制表示法从遍历到到过程
1 | void print(const vector<int>& s) { |
-
代码实现 —— 二进制表示法
非常简单,直接遍历所有比小的整数即可
1 | // O(2^N) |
二进制表示法子集遍历的一些其他技巧
- 遍历任意整数mask代表的集合的子集
1 | // O(2^(mask的二进制表示中1的个数)) |
(j-1)&mask相当于把j的最低位1置为0,同时把该位右边所有的属于mask集合的位设置为1,所以(j-1)&mask是小于j的mask子集中最大的一个,因此这个遍历能够遍历到所有的mask子集;该实现与迭代遍历-数组表示法中的代码思路相同。
- 遍历所有元素个数为k的子集
参考:https://www.cnblogs.com/JihuiPeng/p/13666678.html
1 | int mask = (1 << k) - 1; |
代码拆解:
1 | // 找到字典序最小的1的位置,1100 1100 → 0000 0100 |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Comment