先注意一个点:如果理不清位运算的优先级,就一定要在执行运算时,该带括号的带括号!!!!!
一.基础位运算
x << n:奉行多退少补的原则:假设数据补码长度为8位,将n的二进制下补码 所有位向**左移动n位,并在右边补上n个0。此时统计位数,如果超过8位,则将最左侧超过的几位丢弃。<<**相当于该数十进制下*2^n
eg:
5 << 1 → 101 -> 1010 → 十进制 10(5×2)
5 << 2 → 101 -> 10100 → 十进制 20(5×4)
-2 << 2 -> 1111 1110 -> 111 11000 -> 十进制 -8 (-2 * 4)
x >> n:将n的二进制下补码所有位向右移动n位,并在左边补上n个++符号位!!!++依旧要将右边超位数几位丢弃。>>相当于该数十进制下/2^n
eg:
-8 >> 2 -> 1111 1000 -> 1111 1110 -> 十进制 -2 (-8 / 4)
&,|,^:

~:按位取反。对数据补码的每一位都取反,包括符号位!!
二.给一个数n,确认它二进制的第x位是0还是1
为了方便观察,我们假设将n二进制每一比特位倒置在一个数组里,那么它的下标就代表它是第几位。

随后将n右移x个位置,即把第x个位置移到最后一位,然后再将右移以后的n与1按位与(&),两个数按位与的规则是将两个数化成二进制,每一位一一对应,有0则是0。
假如数据位数为32位:

最终结果为1,则第x位是1。
三.将n二进制表示的第x位修改为1
将x位的数按位或(|)一个1,其余数字要保持不变,就按位或上一个0。要获得满足前面要求的一个数,该怎么办呢?只需将1<<x就行。

随后用n|=(1<<x)即可完成修改。相当于把1挪到第x位,然后再进行按位或。

二三实操:只出现一次的数Ⅱ
https://leetcode.cn/problems/single-number-ii
https://leetcode.cn/problems/single-number-iin是所有数中,除a以外,其余数的种类之和。除a以外其余数均出现三次,因此他们的总数是3n。

注:同一位置指的是同一比特位。
将同一比特位所有数之和%3,得到的结果与a在同一比特位下的值一一对应:

根据以上的信息,我们可以得到算法:
1.求和:实际是求该位下1的个数。
2.用每一位的和分别%3,结果便得到a在同一位下对应的数字,再用取模的结果依次修改ret的每一位,最终ret的每一位就表示a的二进制下的每一位。就得到了a。
3.求和和修改操作就用到了二,三两个小巧思。其中求和的思想是先确认第i位的数是0是1,如果是1,就++sum,以达到统计1个数的目的。而修改操作则是在ret初始值为0的前提下,假如确定第i位a所对应的数字位1,也就是和模3的值为1,就把ret第i位的值改为1。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for(int i = 0;i < 32;i++)//遍历int每一位,并完成ret的修改。
{
int sum = 0;//统计每一位所有数之和。
for(auto e: nums)//求nums里所有数第i位的和
{
if(((e >> i) & 1) == 1)
sum++;
}
sum %= 3;
if(sum == 1) ret |= 1 << i;//修改
}
return ret;
}
};
其实我们可以设计通法,n个数相同,在求和取模的时候就模n

四.将一个数n的二进制数表示的第x位修改为0
**将n的二进制形式&上一个除了第x位是0,其余位均为1的二进制数。**那么怎么保证该二进制数,除了第x位是0,其余全为1呢?
答案是将1左移(<<)x位的结果取个反(~)就行了:

五.lowbit

注意:-n是先对n按位取反,然后再加1的成果。
六.干掉n二进制表示中最右侧的1

n-1时,会去前面的位借位,一直借到最右边的那个1处停止,此时最右边的1就会因借位变成0。
本质:

七.异或(^)操作的运算率
注意:异或玩的就是不进位相加。然后它满足交换律和结合率。
