c++位运算中最常用的两种操作

1,784 阅读3分钟

求整数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++中有关位运算的操作也就能轻松搞定了