如何将bitset拆分为数组?枚举项较多时有没有更简便高效的实现方法
针对大量位掩码枚举的快速提取方案
原来的逐位遍历方案时间复杂度为O(N),N为枚举总位数,当枚举项达到1000个时,最坏情况需要循环1000次,性能较差。优化核心是将时间复杂度降到O(K),K为当前掩码中置1的位的数量,和总枚举数无关,哪怕有1000个枚举项,只要置位的只有几个,就只循环几次。
方案1:短掩码场景(总位数<=64位)
直接利用硬件支持的位操作指令,提取最低位的1后清除,循环次数等于置位的位数:
// 以32位掩码为例,64位可换用uint64_t和对应内置函数 uint32_t val = A | C | F; while (val != 0) { // 提取当前最低位的1,直接对应枚举值 uint32_t curr_enum = val & -val; // 此处写加入数组的逻辑 // arr_push(arr, curr_enum); // 清除已处理的最低位1,进入下一轮 val ^= curr_enum; }
不同环境的适配说明:
- GCC/Clang环境:32位可用
__builtin_ctz(val)直接获取最低位1的位置,64位用__builtin_ctzll(val) - MSVC环境:对应使用
_BitScanForward、_BitScanForward64内置函数 - C23及以上标准:可直接使用标准库
<stdbit.h>中的ctz、ctzll函数,无需依赖编译器扩展
方案2:长掩码场景(总位数>64位,比如1000个枚举项)
1000个枚举项需要至少1000个比特位存储,通常会封装为uint64_t数组(1000位需要16个uint64_t,共1024位足够容纳),优化后只遍历非0的存储块,跳过全0块:
// 假设掩码存在uint64_t mask_arr[16]里,共1024位对应最多1024个枚举项 for (int i = 0; i < 16; i++) { uint64_t block = mask_arr[i]; if (block == 0) continue; // 全0块直接跳过,不需要遍历内部位 // 处理当前非0块里的置位位 while (block != 0) { uint64_t lsb = block & -block; int bit_pos = i * 64 + __builtin_ctzll(lsb); uint64_t curr_enum = 1ULL << bit_pos; // 此处写加入数组的逻辑 // arr_push(arr, curr_enum); block ^= lsb; } }
该方案下,哪怕总共有1000个枚举项,只要大部分位都没有置位,大部分存储块都会被直接跳过,性能远高于逐位遍历。
内容的提问来源于stack exchange,提问作者aaaa
相关产品推荐
相关产品推荐

