标准库中的std::bitset,利用若干个uint32_t或者uint64_t作为存储单元,来表示多个位置的0-1状态,可以节约很多的空间。例如,有31组开关,最少可以只用一个uint32_t来存储这31组开关的状态,只占用4个字节。
现在的需求是,对于这31组开关,依次找到其中打开的,做一系列的处理。标准的std::bitset并没有提供遍历的接口。
在SGI版本的实现中,std::bitset有_Find_First()和_Find_Next()的接口,可以通过如下代码遍历:
std::bitset<31> bs{};
...
for(size_t pos = bs._Find_First(); pos < bs.size(); pos = bs._Find_Next(pos)){
...
}
但这两个接口不是std::bitset必须实现的,visual studio版本并没有这个接口,并且,上述书写方式也比较繁琐,容易出错,用range-based for会更加清爽。
对于非SGI版本,首先要实现FindFirst和FindNext。FindFirst本质就是寻找一个数最低位1的位置,而FindNext则是屏蔽掉当前位置之前的所有1,再做FindFirst操作。
这里我们可以利用一个位运算的性质:对于一个数n,n & (-n) 即可屏蔽掉除最低位的1以外的所有1。因为-n即n取反再加1,所以比最低位1高的位置,因为取反再取与,会变成0;比最低位1低的位置,必然全为0,取反之后全为1,加1之后自然又全为0,最终取与只剩下最低位1。例如:6 (0b0110),-6(0b1010),取与后即为2(0b0010),最低位1的位置为1。利用这个性质,构建一个哈希表,保存所有的2的幂次和其对应幂次的值(log2,也就是低位上有多少个0)便于查找。输入为0的时候,返回64,表示全部都是0(64个)。
uint8_t CountTailingZeros(uint64_t num) {
static const auto && pos_map = []() {
std