前言

LeetCode死磕系列五: 位运算

位运算,在性能上是超过加减乘除的,只不过,费些脑细胞。基础的与& 或| 异或^ 运算,以及左移动、右移。

常用操作

&、| 、^、>>、<<、>>>

假设整数变量 A 的值为 60 和变量 B 的值为 13:

操作符 描述 例子
如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
\ 如果相对应位都是 0,则结果为 0,否则为 1 (A \ B)得到61,即 0011 1101
^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
<< 按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000
>> 按位右移运算符。左操作数按位右移右操作数指定的位数。 A >> 2得到15即 1111
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 A>>>2得到15即0000 1111
  1. 异或

    2 ^ 2 = 0;

  2. 0 & 1 = 0;

    1 & 1 = 0;

n & (n-1)

10000000;

01111111;

     <----- binary ---->
 n      n    n-1   n&(n-1)
--   ----   ----   -------
 0   0000   0111    0000 *
 1   0001   0000    0000 *
 2   0010   0001    0000 *
 3   0011   0010    0010
 4   0100   0011    0000 *
 5   0101   0100    0100
 6   0110   0101    0100
 7   0111   0110    0110
 8   1000   0111    0000 *
 9   1001   1000    1000
10   1010   1001    1000
11   1011   1010    1010
12   1100   1011    1000
13   1101   1100    1100
14   1110   1101    1100
15   1111   1110    1110

可以看到,其第一个方式是用于判断是否为2的幂次,n&(n-1)的结果为0的话,则为2的幂次。

从运算结果分析(以12为例):

12二进制表示为:1100,减去1之后,为1011,可以发现,减去1之后,是将该数字的二进制的最右边的1改为0,n&(n-1)之后的结果为1000,比较11001000,这个操作可以总结为:将当前数字二进制最右边的1去除。最右边的1左边部分不变。所以,也可可以用于统计数字的二进制中1的个数问题

补码:

~x + 1 是 x的补码;

   补码
1 + 11111111111111111 = 000000000000000000;
2 + 11111111111111110 = 000000000000000000

(s | 1<<k)

s: 集合(二进制表示包含的元素)

集合元素$s = {1,4,6}$ 则 $s = 1010010{2} = 82{10}$

k: 元素

将元素k放入集合s

举例说明

当前$s=82$, 此时$k=2$,将$k$放入集合$s$中

  1. $(1<<k) = 4 = 100_{2}$
  2. $(s | 1<< k) == 1010110{2} == 86{10}$

神奇的操作,适用于压缩状态dp

LeetCode & 剑指offer 位运算题目整理

  • 面试题15:二进制中1的个数
  • 136 Single Number

LeetCode 位运算题目整理

面试题15:二进制中1的个数

public static int numberOf(int n){
        // n & (n - 1) 会消除 n 中最后一位中的 1。
        int res = 0;
        while (n != 0){
            res++;
            n &= (n-1);
        }
        return res;
    }

136 Single Number

这题是去除数组中的重复元素(给的测试集是只有一个不重复的元素),用了异或运算,

思路:

相等的数相异或结果为0,0和任何数相异或结果为这个数本身。根据这个特点,可以先设置一个初始值为0,然后分别和数组中的每个元素相异或,最后保留的肯定为唯一一个不重复的值。

相等的元素相异或的值为0,不是特指相等的元素必须相连,也可以不相连。

public singleNumber(int[] nums){
    int res = 0;
    for( int i : nums){
        res ^= i;
    }
    return res;
}

References