美文网首页
位运算系列

位运算系列

作者: 普朗tong | 来源:发表于2020-04-17 13:15 被阅读0次

    位操作符

    & 与运算 两个位都是 1 时,结果才为 1,否则为 0,如

    1 0 0 1 1   
    1 1 0 0 1
    ------------------------------
    1 0 0 0 1
    

    | 或运算 两个位都是 0 时,结果才为 0,否则为 1,如

    1 0 0 1 1  
    1 1 0 0 1
    ------------------------------
    1 1 0 1 1
    

    ^ 异或运算,两个位相同则为 0,不同则为 1,如(可以理解为不进位的加法操作)

    1 0 0 1 1  
    1 1 0 0 1
    -----------------------------
    0 1 0 1 0
    

    ~ 取反运算,0 则变为 1,1 则变为 0,如

    1 0 0 1 1
    -----------------------------
    0 1 1 0 0
    

    左移运算>>,向左进行移位操作,高位丢弃,低位补 0,如

    int a = 8;
    a << 3;  
    移位前:0000 0000 0000 0000 0000 0000 0000 1000
    移位后:0000 0000 0000 0000 0000 0000 0100 0000
    

    右移运算<<,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位,如

    unsigned int a = 8;
    a >> 3;
    移位前:0000 0000 0000 0000 0000 0000 0000 1000
    移位后:0000 0000 0000 0000 0000 0000 0000 0001
    ​
    int a = -8;
    a >> 3;
    移位前:1111 1111 1111 1111 1111 1111 1111 1000
    移位前:1111 1111 1111 1111 1111 1111 1111 1111
    

    常见位运算问题

    1.位操作实现乘除法

    • 数 a 向右移一位,相当于将 a 除以 2;数 a 向左移一位,相当于将 a 乘以 2
    int a = 2;  
    a >> 1; ---> 1  
    a << 1; ---> 4  
    

    2.位操作交换两数

    • 位操作交换两数可以不需要第三个临时变量,虽然普通操作也可以做到,但是没有其效率高

      一个数异或另一个数两次等于没有异或

    //普通操作
    void swap(int &a, int &b) {
      a = a + b;
      b = a - b;
      a = a - b;
    }
    
    //位与操作
    void swap(int &a, int &b) {
      a ^= b;
      b ^= a;
      a ^= b;
    }
    

    3.位操作判断奇偶数

    • 只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数
    if(0 == (a & 1)) {
     //偶数
    }
    

    4.位操作交换符号

    • 交换符号将正数变成负数,负数变成正数
    int reversal(int a) {
      return ~a + 1;
    }  
    
    整数取反加1,正好变成其对应的负数(补码表示);负数取反加一,则变为其原码,即正数
    

    5.位操作求绝对值

    • 整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(
      整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作
    int abs(int a) {
      int i = a >> 31;
      return i == 0 ? a : (~a + 1);
    }
    

    位运算实战技巧系列(Golang实现)

    1.技巧1

    x & (x - 1) 用于消去x最后一位的1
    x = 1100
    x - 1 = 1011
    x & (x - 1) = 1000
    
    • 用 O(1) 时间检测整数 n 是否是 2 的幂次。
      2的幂
      思路:
      n如果是2的幂次,则n应该满足两个条件
      • n>0
      • n的二进制表示中只有1个1
        代码实现:
    func isPowerOfTwo(n int) bool {
            return n > 0 && n&(n-1) == 0
    }
    
    • 计算在一个 32 位的整数的二进制表式中有多少个 1。
      二进制表示中质数个计算置位
      思路解析:由n&(n-1)消去最后一位1可知,不断使用 x & (x - 1) 消去x最后一位的1,计算总共消去了多少次即可
      代码实现:
    func countPrimeSetBits(L int, R int) int {
        primes := []int{2, 3, 5, 7, 11, 13, 17, 19}
        var res int
        for i := L; i <= R; i++ {
            k := i
            count := 0
            for k != 0 {
                k &= k - 1
                count++
            }
            for __, v := range primes {
                if v == count {
                    res++
                }
            }
        }
        return res
    } 
    
    • 如果要将整数A转换为B,需要改变多少个bit位?
      思路解析:如果A转化为B,首先获取A和B中二进制表示的bit位不相同的部分(采用异或实现),然后计算不相同的个数(即1的个数)即为结果

    2.技巧2

    a ^ b ^ b = a
    a ^ a = 0
    a ^ 0 = a
    异或运算满足交换律和结合律
    
    • 数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数
      求数组中只出现一次的数字
      思路解析:因为只有一个数恰好出现一个,剩下的都出现过两次,所以只要将所有的数异或起来,就可以得到唯一的那个数。
      代码实现:
    func singleNumber(nums []int) int {
        res := 0
        for _, v := range nums {
            res ^= v
        }
        return res
    }
    
    • 数组中,只有一个数出现一次,剩下都出现三次,找出出现一次的数
      只出现一次的数字 II
      思路解析:
      (1)采用HashMap做
      (2)采用位运算做
      代码实现:
    //采用HashMap
    func singleNumber(nums []int) int {
        m := make(map[int]int)
        for _, v := range nums {
            m[v]++
        }
        for k, v := range m {
            if v == 1 {
                return k
            }
        }
        return 0
    }
    
    //采用位运算
    func singleNumber(nums []int) int {
        res, count := 0, 0
        for i := 0; i < 64; i++ {
            count = 0
            for j := 0; j < len(nums); j++ {
                count += (nums[j] >> i) & 1
            }
            res |= (count) % 3 << i
        }
        return res
    }
    
    //采用位运算
    func singleNumber(nums []int) int {
        a,b := 0,0
        for _,v := range nums {
            a = (a^v) & ^b
            b = (b^v) & ^a
        }
        return a
    }
    
    • 两整数之和
      两整数之和
      思路解析:
      (1)首先计算a^b
      (2)计算a+b产生的进位
      代码实现:
    func getSum(a int, b int) int {
    
        if b == 0 {
            return a
        } else {
            sum := a ^ b
            sum += (a & b) << 1
            return sum
        }
    }
    

    相关文章

      网友评论

          本文标题:位运算系列

          本文链接:https://www.haomeiwen.com/subject/uivevhtx.html