Skip to content

Latest commit

 

History

History
 
 

077. Subsets

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

乍一看,还以为又是排列组合题,但细一看,发现并未要求排列,要求保持 non-descending 的顺序。而且 subset 参差不齐,却又涵盖各种情况。

以 S = [1,2,3] 为例,其 subsets 有 8 个,全无,全有,两个,一个。这样的排列不正好是二进制的节奏吗? 8 == 2^3 即从 000 到 111。列个表:

result bit manipulation bit value
[3] [0,0,1] 1
[1] [1,0,0] 4
[2] [0,1,0] 2
[1,2,3] [1,1,1] 7
[1,3] [1,0,1] 5
[2,3] [0,1,1] 3
[1,2] [1,1,0] 6
[] [0,0,0] 0

是否一目了然呢?

首先,我们拿到该 set,应进行排序,保证 non-descending,然后计算返回数组的长度:

size = std::pow(2, set.size());

接下来的难点在于,如何按照位分布,来组合 vector 的元素。譬如现在我们知道 size = 8 :

vector<vector<int>> retv(size); // 先构造返回的二维数组
for (int i=0; i<size; ++i) // 遍历每一个 bit value, 同时也是遍历 retv
    for (decltype(set.size()) j=0; j<set.size(); ++j) // 遍历 set 中的每一个 element.
        // 如果该位 == 1
        retv[i].push_back(set[j]);

好了,问题来了,注释的那句话该怎么写? 如何判断该位的值?

举例:假如现在 bit value 为 2,那么其二进制位为: [0,1,0]

第一次迭代我们希望得到 0, 第二次希望得到 1,第三次希望得到 0. 这个过程不正好是二进制数右移的过程吗?

010 >> == 001 >> == 000
  ^         ^         ^

每一次右移动的最低位值即为我们想要的,如何取得?简单,与操作即可:

(i >> j) & 0x1

这样我们就能够得知该位是 true 还是 false 了。

最后一个问题在于,由于是 push_back,所以顺序与我们所列的表格有出入,如 bit value == 1 的时候,result 成了 [1],所以如果要严格遵循表格顺序,应该是:

retv[i].push_back(set[set.size() - j - 1]);

但显然这样很不简洁,而且保持表格的顺序并无必要,所以按上面的写法就 OK 了。