1611:使整数变为0的最少操作次数


我们需要确定在0 ≤ n ≤ 10⁹的范围内,整数n转换为二进制后最多有多少位。
二进制位数的基本概念
一个正整数n的二进制表示的位数可以通过以下公式计算:
位数=⌊log2n⌋+1
这个公式的含义是:n的二进制位数比n的二对数(向下取整)多1。这是因为:
-
如果n是2的幂次方(例如1, 2, 4, 8, ...),那么log₂n是整数,位数就是log₂n + 1。
-
如果n不是2的幂次方,那么log₂n不是整数,向下取整后加1得到位数。
计算10⁹的二进制位数
我们需要找到最大的n在范围内,即n = 10⁹。计算其二进制位数:
位数=⌊log2109⌋+1
首先计算log₂10⁹:
log2109=9×log210
我们知道:
log210≈3.321928
因此:
9×log210≈9×3.321928≈29.897352
然后取 floor:
⌊29.897352⌋=29
最后加1:
29+1=30
因此,10⁹的二进制表示有30位(数目),对应下标0...29。
while(n) {
int id1 = -1, id2 = -1;
for(int i = 30; i >= 0; i--) { // 从高位开始遍历,寻找 1
if((1 << i) & n) {
if(id1 == -1) id1 = i; // 记录较高位的 1
else if(id2 == -1) id2 = i; // 记录较低位的 1
else {
break; // 都找到了就 break
}
}
}
这段代码的目的是:在 n 的二进制表示中,从高位到低位,找到最高的两个 1 的位置,分别记为 id1 和 id2。
(1 << i) & n
作用 :判断 n 的第 i 位是否为 1 。
-
& n:++与n按位与,如果结果非零,说明n的第i位是 1。++ -
按位与就是"两个数的二进制对齐,逐位做逻辑与",结果只有对应位同时是 1 的位置才是 1。
n^=(1<<id1);

class Solution {
public:
int minimumOneBitOperations(int n) {
int cnt[30]; //10⁹的二进制表示有30位(数目),对应下标0...29
int res=0;
for(int i=0;i<30;i++) cnt[i]=(1<<(i+1))-1; //将规律 1 的结果预处理出来存在数组 cnt[i] = 2^(i+1) - 1
while(n){
int id1=-1,id2=-1;
for(int i=29;i>=0;i--){ //从高位开始遍历,寻找 1
if((1<<i)&n){ //判断 n 的第 i 位是否为 1,按位与(包括第0位)
if(id1==-1) id1=i; //记录较高位的 1
else if(id2==-1) id2=i; //记录较低位的 1
else{
break; //都找到了就 break
}
}
}
if(id2==-1){ //只找到一个
n^=(1<<id1); //第 id1 位被反转,其余位保持不变
res+=cnt[id1]; //规律1
}
else{
n^=(1<<id1);
n^=(1<<id2);
res+=(cnt[id1]-cnt[id2]); //规律2
}
}
return res;
}
};