求整数n二进制表示中的第k位
💯二进制常识
比如n的十进制表示为15,其二进制表示就是1111(注意二进制是从右向左算的,最右边的是个位,算的话:个位是第0位,十位是第1位,百位是第2位 ......)
💢把第k位放到个位上
那么如何由整数n到二进制表示中的第k位呢? 整数直接操作计算机二进制的核心操作:位运算,c++中提供的位运算符之一有 '<<' (位右移运算符)。
How to use it:比如 3 >> 1 就是将3的二进制向右移移一位(右移就是箭头指向右边哦)。11(3的二进制) 向右移移位变成1,低位自动消失,那么如果 n >> k就是向右移动k位(原来的第0 ~ 第k - 1个数被去掉了),那么第k个数就到了个位的位置。 【注】这里的位运算操作是不会影响原来变量的值的,了解这点非常重要。
int m = 10; // 二进制1010
cout << (m >> 1); // 5 ,二进制101
cout << m; // 10
💥到个位后,如何取出
"与"运算
这里的个位是指二进制中的个位,我们都知道取十进制的个位很简单:x%10即可。那么怎么把二进制的个位取出来呢? 这里应该想到 “按位与&” 运算,&运算的本质就是将二进制的各个数位都做与操作,其中0为false,1为true,空位都按0来处理。
比如:10 & 1,10的二进制表示为1010,1的二进制表示为1,那么"10 & 1"更加容易理解的形式就是 1010 & 0001 ,false & false 还是 false,true & true 还是 true,只有false & true 时才会变为false。所以最终与的结果为 0000,也即0。 计算法则实际上也就是:与上0相当于把那位数清0,与上1相当于把那位保留。
与运算取个位
重点来啦!!! 根据“按位与”的特性(与1保留,与0清0),我们可以用它来 取一个数二进制表示中的指定位。 让该数与的那个数想要的指定位为1,其他所有位都为0即可,但要注意&运算的左右两边都要放由二进制计算过的十进制数。
比如,想要取出10(二进制1010)的第3位1,我们就可以10 & 8(8的二进制为1000),结果就是8,但这并不是我们想要的结果啊,我们想要的是二进制本身的那个数,但现在却是1 * 2^(4 - 1),所以: 要直接取到二进制中本身位的那个数,最好把那个数放到个位(不然就会被转为10进制数形式输出),然后用'& 1'将其取出,这也就是上面我们要把第k个数移动到个位的原因了(🧐绝对重点!)。(注:& 1一定是取的个位,因为它可以是01,001,0001... 与另一个数相匹配,一个位上的数一定是1)
int m = 10;
cout << (10 & 1) << endl; // 0
// 原理为 1010 & 0001 ---> 0000,最后给我的就是0000 * 2^0 等于 0
💫正式解决
有了以上三个运算基础后,就可以正式地解决问题了,求二进制表示中的第k位:
①. 先把二进制中的第k位数移动到个位 :n >> k;
②. 然后再看个位是几 : (n >> k) & 1
因为" >> " 的优先级比 & 高,所以整个步骤可以合并为 :n >> k & 1
这样我们就轻松地求出了整数n二进制表示中的第k位啦💐
返回x的最后一位1: lowbit(x)
💭lowbit实现讲解
这里指的是二进制中的最后一位出现的1所对应的值,直接给结论: x & -x
原理是什么? c++中-x和x取反加1是等价的(~x+1实际上就是补码,二进制中是用补码去表示负数的),所以: x & -x == x & (~x + 1) 实例论证: x = 1010 ... 100 ... 0
~x = 0101 ... 011 ... 1
~x + 1,二进制加法,遇2进1:
~x + 1 = 0101 ... 100 ... 0 现将x & (~x + 1) ,根据我们上面讲解的“按位与”操作可以看出结果为:
x & (~x + 1) = 0000 ... 100 ... 0,原来最后一位的1前面的数全部变为0了,后面的数也变为0了,只剩它一个,此时整个二进制数的值也对应了它的值!
【注】感兴趣地同学可以思考一下为什么 - x == ~x + 1?
提示: -x == 0...32个0 - x =二进制中= 10...32个0(共33位) - x
👣lowbit应用
lowbit最常见的应用就是统计x二进制表示中1的个数,思想是: 每次都把x的最后一位1减掉,当x == 0时,里面就没有1了,减了多少次,就说明里面还有多少个1! 现给一个题目:
给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。
输入格式
第一行包含整数 n。
第二行包含 n 个整数,表示整个数列。
输出格式
共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。
代码实现
#include<iostream>
using namespace std;
int lowbit(int x) {
return x & -x;
}
int main() {
int n;
cin >> n;
while(n --) {
int x;
cin >> x;
int res = 0;
while(x) x -= lowbit(x), res ++; // 每次都减去x的最后一位1对应的值,然后计数变量++,直到减到0为止
cout << res << ' ';
}
return 0;
}
小结
最后记住位运算的两个基本问题:
① 求整数n二进制表示中的第k位:n >> k & 1
② lowbit求整数x二进制表示中最后一位出现的1所对应的值:x & -x
如果能够深刻理解上面两个问题,其他c++中有关位运算的操作也就能轻松搞定了