2859. 计算 K 置位下标对应元素的和
给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
请你用整数形式返回 nums 中的特定元素之 和 ,这些特定元素满足:其对应下标的二进制表示中恰存在 k 个置位。
整数的二进制表示中的 1 就是这个整数的 置位 。
例如,21 的二进制表示为 10101 ,其中有 3 个置位。
示例 1:
输入:nums = [5,10,1,5,2], k = 1
输出:13
解释:下标的二进制表示是:
0 = 0002
1 = 0012
2 = 0102
3 = 0112
4 = 1002
下标 1、2 和 4 在其二进制表示中都存在 k = 1 个置位。
因此,答案为 nums[1] + nums[2] + nums[4] = 13 。示例 2:
输入:nums = [4,3,2,1], k = 2
输出:1
解释:下标的二进制表示是:
0 = 002
1 = 012
2 = 102
3 = 112
只有下标 3 的二进制表示中存在 k = 2 个置位。
因此,答案为 nums[3] = 1 。提示:
1 <= nums.length <= 10001 <= nums[i] <= 1050 <= k <= 10
思路
常规遍历数组下标判断
位操作起飞
代码
常规遍历数组下标判断
class Solution {
public int sumIndicesWithKSetBits(List<Integer> nums, int k) {
int result = 0;
for (int i = 0; i < nums.size(); i++) {
if (Integer.bitCount(i) == k) {
result += nums.get(i);
}
}
return result;
}
}位操作起飞
class Solution {
public int sumIndicesWithKSetBits(List<Integer> nums, int k) {
if (k == 0) {
return nums.getFirst();
}
int n = nums.size();
int answer = 0;
int i = (1 << k) - 1;
while (i < n) {
answer += nums.get(i);
i = next(i);
}
return answer;
}
// 获取二进制中 1 的数量和 num 相同且大于 num 的最小数字
private int next(int num) {
int x = num & -num;
int t = num + x;
return t ^ ((num ^ t) / x >> 2);
}
}方法步骤详解
核心
private int next(int num) {
int x = num & -num; // 步骤1:找到最右侧的1位
int t = num + x; // 步骤2:翻转最右侧连续的1位
return t ^ ((num ^ t) / x >> 2); // 步骤3:调整剩余位
}这个方法实现了一个巧妙的位操作算法,用于找到给定整数 num 的下一个具有相同数量1位的更大数字。这种操作在组合数学和位操作中很有用。
步骤1:找到最右侧的1位
int x = num & -num;-num是num的二进制补码表示num & -num会保留num中最右边的1,其他位都置为0例如:
num = 01101000(二进制),则x = 00001000
步骤2:翻转最右侧连续的1位
int t = num + x;将
num加上x会翻转从最右边开始的连续的1位例如:
num = 01101000,x = 00001000,t = 01101000 + 00001000 = 01110000
步骤3:调整剩余位
return t ^ ((num ^ t) / x >> 2);这部分最复杂,分解来看:
num ^ t:计算num和t不同的位对于上面的例子:
01101000 ^ 01110000 = 00011000
/x:将结果右移log2(x)位(因为除以x相当于右移x的1的位置)00011000 / 00001000 = 00000011(即右移3位)
>> 2:再右移2位00000011 >> 2 = 00000000
t ^ ...:最后与t进行异或01110000 ^ 00000000 = 01110000
完整示例
以 num = 0b01101000 (104) 为例:
x = num & -num = 01101000 & 10011000 = 00001000(8)t = num + x = 01101000 + 00001000 = 01110000(112)num ^ t = 01101000 ^ 01110000 = 00011000(24)(num ^ t)/x = 00011000 / 00001000 = 00000011(3)>> 2=00000000(0)t ^ 0 = 01110000(112)
所以 next(104) 返回 112,它们的二进制表示分别是 01101000 和 01110000,都有3个1位。