神奇的位运算


运算符介绍

运算符 操作 介绍
& 位数据的值同为1,该位取1,不同取0
| 位数据两者有一个为1,该位取1,否则取0
~ 对位数据取反
^ 异或 两者位数据不同取1,否则取0 (0^n = n n为任意数)
<< 左移 左移后右边位补0
>> 右移 右移后左边位补0

运算小技巧

1、判断奇偶数

常规思路:

if(n % 2 == 1)  //n为奇数

位运算技巧:

-- 利用&运算符,与1进行运算,其它位数据均为0

if(n & 1 == 1) //n为奇数
2、交换两个数字

常规思路:

//a和b交换
int temp = a;
a = b;
b = temp;

-- 使用异或^运算,相同的两个数异或结果为0:

//a和b交换
a = a^b; // a = a^b
b = a^b; // b = (a^b)^b = a
a = a^b; // a = (a^b) ^ (a^b)^b

假设a = 5, b = 7,推导过程见注释:

3、统计数字二进制表示中1的个数

-- 使用移位计算:

public int GetNumber1InBinary(int num){
    int res = 0;
    for(int i = 0; i< 32; i++){
        if(num & (1 << i) != 0){
            res++;
        }
    }
    return res;
}
4、只出现一次的一个数字

例如:1, 2, 3, 5, 3, 2中,仅有只有一个1,其它数字都是2个;

--使用异或运算,两个相同的数字异或为0,任何数字与0异或还是其自身。

public int GetSingleNumber(int[] nums){
    int res = 0;
    foreach(var num in nums){
        res ^= num;
    }
    return res;
}
5、只出现一次的一个数字(升级版)

例如:将上题中其它数字都改为3个,则将无法使用异或运算直接运算:

这次利用整除求余,先看图:

即,每位总会多出一个0或1不满足是3的倍数,循环完将各位数据求和便可得到最终的答案,这样的时间复杂度为O(n).

public int GetSingleNumber(int[] nums){
    int res = 0;
    for(int i = 0; i< 32; i++){
        int sum = 0;
        for(var num in nums){
            if(((num > 1) & 1) == 1){
                sum++;
            }
        }
        if(sum % 3 == 0){
            res += (1<