leetcode 之位运算

243 阅读19分钟

Js源码

github.com/zsjun/leetc…

位运算

191. Number of 1 Bits

Write a function that takes an unsigned integer and return the number of '1' bits it has (also known as the Hamming weight).

Example 1:

Input: 00000000000000000000000000001011 Output: 3 Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits. Example 2:

Input: 00000000000000000000000010000000 Output: 1 Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit. Example 3:

Input: 11111111111111111111111111111101 Output: 31 Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.

Note:

Note that in some languages such as Java, there is no unsigned integer type. In this case, the input will be given as signed integer type and should not affect your implementation, as the internal binary representation of the integer is the same whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 3 above the input represents the signed integer -3.

思考过程
1 关键点 n & n-1 相当于去掉最右边的1
console.log(4294967293 & 4294967292) = -4
js的&运算会首先把
1.11111111111111111111111111111010000000000000000000001000001111;4294967293
1.11111111111111111111111111111000000000000000000000001000001111;4294967293
当执行后
1111111111111111111111111111100 = -4

  1. Single Number III

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

Example:

Input: [1,2,1,3,2,5] Output: [3,5]

Note:

The order of the result is not important. So in the above example, [5, 3] is also correct. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?

思考路程
1 希望利用 137 的思路来获得,同样是获取每列有多少个 1,可是因为有两个,所以这里肯定得有两次,一次找出一个,可是还没想来如何进行找出
2 题解 假设[1,2,1,3,2,5] 2.1 首先一次通过异或遍历,然后肯定是两个不同的3和5异或,结果就是1010,所以3和5肯定是有一个1的位置两者是不同的,比如3和5,肯定最高位就不同,一个是0,一个1,否则不可能异或出1
2.2
Let a and b be the two unique numbers
XORing all numbers gets you (a xor b)
(a xor b) must be non-zero otherwise they are equal
If bit_i in (a xor b) is 1, bit_i at a and b are different.
Find bit_i using the low bit formula m & -m (这里是固定的,获取第一个1出现的位置,比如10&-10 = 2)
Partition the numbers into two groups: one group with bit_i == 1 and the other group with bit_i == 0.
a is in one group and b is in the other.
a is the only single number in its group.
b is also the only single number in its group.
XORing all numbers in a's group to get a
XORing all numbers in b's group to get b
Alternatively, XOR (a xor b) with a gets you b.

时间复杂度O(n)空间复杂度O(1)

心得
1 位运算感觉套路好多,最后还是要落到1和0上面

137. Single Number II

Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,3,2] Output: 3 Example 2:

Input: [0,1,0,1,0,1,99] Output: 99

思考过程
1 本来想用异或,感觉没什么思路,后来想先排序,那好像就没位运算什么事了。

2 题解
2.1参数的定义
k 是指统计某一列中1出现的次数。

2.2 首先,我们先来考虑只有一位的情况。假设我们有一个整数数组,它的元素只有1位二进制(0或者1),我们需要统计数组中1的数量,当1的数量达到某一个值,比如k时,计数重置为0,重新开始统计(这个k就是问题描述中的k)。为了记录到目前为止我们统计了多少个1,我们需要一个计数器。假设计数器有m位二进制:xm, ..., x1(从高位到低位)。我们至少可以总结出计数器的四个特性:

计数器有一个初始值,为了简单起见,置为0;
对于数组中的每个输入,如果为0,计数器保持不变;
对于数组中的每个输入,如果为1,计数器加1;
为了保证计数器表示的值能大于等于k,例如当计数器为11,也就是当x1=1, x2 = 1的时候,最大表示3, 也就是说当计数器为2位的时候,最大可以表示k的数目是3。这里就可以推论出如果想表示k,至少需要2^m >= k,等式两边取对数:m >= logk。

然后就到了关键部分:当我们扫描数组时,记住这里数组里边的值只有0和1,除此之外没有其它的任何值。
计数器的每一位(x1到xm)是如何变化的?
我们可以使用位操作,为了保证第2个特征,回想一下,那些位操作与0进行运算时,不会改变自身呢?
没错,你说对了:x = x | 0 和x = x ^ 0。
^ 指的是异或,当两者不同且只有一个1的时候为1,其它情况下为0

我们现在有表达式:x = x | i 或者x = x ^ i,i为数组元素(这里i要么是0,要么是1)。
哪一个表达式更好呢?
我们现在还不知道,所以我们来实际试试,看下那个符合我们的要求。

开始,计数器的所有位都初始化为0,即xm = 0, ... , x1 = 0。我们选择的位运算,要保证当扫描到数组元素为0时,计数器的每一位都不变。计数器的值为0,直到扫描到数组第一个为1的元素。
当我们扫描到第一个为1的元素,计数器的每一位为:xm = 0, ..., x2 = 0, x1 = 1。
让我们继续扫描,直到找到第二个为1的元素,这时计数器的每一位为:xm = 0, ..., x2 = 1, x1 = 0。注意,x1由1变为了0。对于x1 = x1 | i,经过2次计数(也就是2次x1 = x1 | 1)后,x1的值仍然为1。所有很明显,我们应该使用x1 = x1 ^ i。 关于x2, ..., xm应该怎么计算呢?
我们要找出x2, ..., xm改变值的条件。以x2做为示例。如果我们再扫描到一个1,想要改变x2的值,x1的值必须为多少呢?
答案是:x1必须为1,因为x1为0的话,把x1由0变为1就可以了,而不必改变x2的值(其实这就是进位的原理,比如十进制要进位,必须要后一位为9)。所以,x2要改变值的条件是,x1和i都为1,对应的算术表达式为x2 = x2 ^ (x1 & i)。同理,xm要改变值的条件是,xm-1, ..., x1和i都为1,对应的算术表达式为:xm = xm ^ (xm-1 & ... & x1 & i)。没错,这就是我们找到的位操作方法。
(这里其实一种比较简单的方法就是直接加1)
然而,我们应该注意到上面的位操作中,计数的范围是从0到2 ^ m - 1。如果k满足k < 2 ^ m - 1,则什么也不需要做。
但是当计数器等于k时,我们需要一种重置机制把计数器重置为0。为了达到这个目的,我们申请一个变量mask,分别对计数器的每一位(xm, ..., x1)和mask进行按位与操作,即xm = xm & mask, ..., x1 = x1 & mask。如果我们能保证,只有当计数器的值等于k,并且统计的都是数组元素为1情况下,mask会为0,我们就达到了目的。我们是如何做到这一点的呢?试着想想用k计数与其他计数的区别。没错,它是统计元素为1的数量!对于每一次计数,计数器的每一位都有唯一的值,可以视为计数器的状态。如果我们把k写成二进制的形式:km, ..., k1,我们可以按照如下的方法来构造mask:
这里其实只是一种方法,应该还有其它方法,

// 如果kj = 1,yj = xj;kj = 0,yj = ~xj。j的取值范围是1到m。
mask = ~(y1 & y2 & ... & ym)

一些例子如下:

k = 3: k1 = 1, k2 = 1, mask = ~(x1 & x2);
k = 5: k1 = 1, k2 = 0, k3 = 1, mask = ~(x1 & ~x2 & x3);

所以算法如下

for (int i : nums) {
    xm ^= (xm-1 & ... & x1 & i);
    xm-1 ^= (xm-2 & ... & x1 & i);
    .....
    x1 ^= i;

    // 如果kj = 1,yj = xj;kj = 0,yj = ~xj。j的取值范围是1到m。
    mask = ~(y1 & y2 & ... & ym);

    xm &= mask;
    ......
    x1 &= mask;
}

2.3 数组元素为32位整数的一般情况
现在我们把数组元素从1位推广到32位。一种最直接的方法是为32位整数中的每一位创建一个计数器(共32个计数器)。你可能在其他的文章中看到过这种解决方案。因为我们使用位操作,也许我们能“共同”管理这32个计数器。说是“共同管理”,是因为m的最小值满足m >= logk时,我们可以用m个32位的整数代替32个m位的计数器。能替代的原因是,位操作只作用于每个位,不同位上的操作是相互独立的(这是很明显的事,是吧?)。这允许我们将32个计数器的对应位放到一个32位整数中。下面的示意图展示了我们是如何做到这一点的。

第一行是一个32位的整数,对于每一位,我们都有一个对应的m位计数器(由向上箭头下方的列所示)。由于32位中的每一位操作都是相互独立的,所以我们可以将所有计数器的第m位放到一个32位整数中(如橙色框所示)。这个32位整数(表示为xm)的所有位都将遵循同样的位操作。由于每个计数器都有m位,所以我们得到了m个32位整数,对应第2部分中定义的x1, ..., xm,只不过是用32位的整数替换了1位的数字。因此,在上面的算法中,我们把x1, ..., xm看做是32位的整数,而不是1位的数字即可。其他的内容,都是一样的,到这里我们就扩展到了32位整数的一般情况了。

2.4 返回值
最后一个问题是,我们应该返回什么值,或者说x1到xm中那个值与我们要返回的值相等。为了得到正确答案,我们需要理解x1, ..., xm这m个32位整数分别代表什么。以x1为例,x1有32位,我们把它标记为r(r的取值为1到32,这里的r是指在x1中的位置,比如 1代表x1中的第一位)。当我们扫描完数组后,x1第r位的值由数组(这里的数组是指输入的nums数组)中所有元素第r位共同决定(因为需要x1的r位不断和数组中传入的数进行异或操作)
(更具体的说,数组中所有元素第r位为1的总数为q,定义q' = q % k,q'的二进制形式为:q'm, ..., q'1,然后根据定义,x1第r位的值等于q'1)。现在你可以问自己这样一个问题:x1的第r位为1意味着什么?

答案是要找到这个1是由哪些数组元素贡献的。一个出现k次的元素会起贡献吗?不会,为什么?因为一个元素要有贡献,它至少必须同时满足两个条件:这个元素的第r位为1,这个元素出现的次数不是k的整数倍。第一个条件很简单。第二个条件来自于这样一个事实:当1的数量等于k时,计数器将会重置为0,这意味着x1中相对应的位会被置为0。对于一个出现k次的元素,同时满足这两个条件是不可能的,所有它不可能贡献。最后,只有出现p (p % k != 0)次的元素才会贡献。如果p > k,那么这个元素前k * p / k次也不会有贡献。因此我们设置p' = p % k,并说元素有效地出现了p'次。

我们把p'用二进制形式表示:p'm, ..., p'1(注意p' < k, 所以m个比特位足够用来表示它)。这里我声明xj等于所求元素的条件是p'j = 1(j的取值为1到m),下面给出快速证明。

如果xj的第r位为1,我们可以肯定的说,所求元素的第r位也为1(否则,没有任何元素能把xj的第r为置为1)。我们只需要证明,xj的第k位为0,所求元素的第k位只能为0。假设xj第k位为0,而所求元素第k位为1,我们来看看会发生什么。扫描结束后,所求元素的这个1将会被统计p'次。根据定义,xj的第k位等于p'j,等于1。这和假设xj的第r位为0冲突。因此,我们得出结论,只要p'j = 1,xj的第r位就总是和所求元素的第r位一样。这对于xj所有位都是成立的(即:r = 1到32都成立),所以我们可以得出结论,p'j = 1时,xj就等于所求元素。

要返回什么值,现在已经很明显了,用二进制形式表示p' = p % k,当p'j = 1时,返回xj即可。总结起来,算法的时间复杂度为O(n * logk),空间复杂度为O(logk)。

附注: 这里有一个把xj的每一位、p'j、和所求元素s的每一位联系起来的通用公式:(xj)_r = s_r & p'j,(xj)_r和s_r分别表示xj和所求元素s的第r位。从公式中,当p'j = 1时,很容易得到(xj)_r = s_r,也就是说p'j = 1时,xj = s,和我们上面说的一样。此外当p'j = 0时,不管所求元素的值为多少,(xj)_r = 0,所以当p'j = 0时,xj都为0。所以我们得到结论:如果p'j = 1,xj = s;如果p'j = 0,xj = 0。这意味着表达式(x1 | x2 | ... | xm)的结果也等于所求元素s,因为这个表达式本质上是所求元素与自己和一些0进行或运算,最终也会等于所求元素。

时间复杂度 O(nlgk) 空间复杂度O(lgk)

371. Sum of Two Integers

Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.

Example 1:

Input: a = 1, b = 2 Output: 3 Example 2:

Input: a = -2, b = 3 Output: 1

思考路程
1 利用 32 位置异或,不断地判断每个位的数字是 0 还是 1 2 题解
2.1 相加但不进位,1^0=1,1^1=0,0^0=0,所以第一步用异或。
2.2 只求进位的结果,只有两个 1 才会进位,所以用&,然后左移 1 位,表示要进的位。
2.3 把前两步的结果再重复 1,2 步,直到没有进位产生,即 b=0

算法复杂度 O(n) 空间复杂度 O(1)

心得
1 位运算的真谛就是要确定每个位置是 0 还是 1

190. Reverse Bits

Reverse bits of a given 32 bits unsigned integer.

Example 1:

Input: 00000010100101000001111010011100 Output: 00111001011110000010100101000000 Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 which its binary representation is 00111001011110000010100101000000. Example 2:

Input: 11111111111111111111111111111101 Output: 10111111111111111111111111111111 Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471 which its binary representation is 10111111111111111111111111111111. Note:

Note that in some languages such as Java, there is no unsigned integer type. In this case, both input and output will be given as signed integer type and should not affect your implementation, as the internal binary representation of the integer is the same whether it is signed or unsigned. In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 2 above the input represents the signed integer -3 and the output represents the signed integer -1073741825.

Follow up:

If this function is called many times, how would you optimize it?

思考路程
1 这里其实只要遵循 2 进制转十进制的方法即可

389. Find the Difference

Given two strings s and t which consist of only lowercase letters.

String t is generated by random shuffling string s and then add one more letter at a random position.

Find the letter that was added in t.

Example:

Input: s = "abcd" t = "abcde"

Output: e

Explanation: 'e' is the letter that was added.

思考路程
1 类似于前面做过的查找数组中的唯一不同的数字的题目

401. Binary Watch

A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59).

Each LED represents a zero or one, with the least significant bit on the right.

For example, the above binary watch reads "3:25".

Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the watch could represent.

Example:

Input: n = 1 Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"] Note:

The order of output does not matter. The hour must not contain a leading zero, for example "01:00" is not valid, it should be "1:00". The minute must be consist of two digits and may contain a leading zero, for example "10:2" is not valid, it should be "10:02".

思考路程
1. 1 如果判断小时和分钟里边等于 1 的个数

405. Convert a Number to Hexadecimal

Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used.

Note:

All letters in hexadecimal (a-f) must be in lowercase. The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character '0'; otherwise, the first character in the hexadecimal string will not be the zero character. The given number is guaranteed to fit within the range of a 32-bit signed integer. You must not use any method provided by the library which converts/formats the number to hex directly. Example 1:

Input: 26

Output: "1a" Example 2:

Input: -1

Output: "ffffffff"

思考路程 1 直接转就可以了

461. Hamming Distance

The Hamming distance between two integers is the number of positions at which the corresponding bits are different.

Given two integers x and y, calculate the Hamming distance.

Note: 0 ≤ x, y < 231.

Example:

Input: x = 1, y = 4

Output: 2

Explanation: 1 (0 0 0 1) 4 (0 1 0 0) ↑ ↑

The above arrows point to positions where the corresponding bits are different. 思考路程 1 先异或,再求异或结果的 1 的位数

476. Number Complement

Given a positive integer num, output its complement number. The complement strategy is to flip the bits of its binary representation.

Example 1:

Input: num = 5 Output: 2 Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2. Example 2:

Input: num = 1 Output: 0 Explanation: The binary representation of 1 is 1 (no leading zero bits), and its complement is 0. So you need to output 0.

Constraints:

The given integer num is guaranteed to fit within the range of a 32-bit signed integer. num >= 1 You could assume no leading zero bit in the integer’s binary representation. This question is the same as 1009: leetcode.com/problems/co…

思考过程
1 比如 5 输出 2,其实就是 5 的二进制 101 和 111 的异或,而 1 输出 0,也是 1 和 1 的异或结果,所以如何或缺 111 呢?

可以利用 Math.pow 来获取

693. Binary Number with Alternating Bits

Given a positive integer, check whether it has alternating bits: namely, if two adjacent bits will always have different values.

Example 1: Input: 5 Output: True Explanation: The binary representation of 5 is: 101 Example 2: Input: 7 Output: False Explanation: The binary representation of 7 is: 111. Example 3: Input: 11 Output: False Explanation: The binary representation of 11 is: 1011. Example 4: Input: 10 Output: True Explanation: The binary representation of 10 is: 1010.

思考过程
1 转成数组还好计算
2 利用异或,比如 1010 异或肯定是 0,但是问题是怎么异或,转成数组异或,可以利用单个单个的计算

762. Prime Number of Set Bits in Binary Representation

Given two integers L and R, find the count of numbers in the range L, R having a prime number of set bits in their binary representation.

(Recall that the number of set bits an integer has is the number of 1s present when written in binary. For example, 21 written in binary is 10101 which has 3 set bits. Also, 1 is not a prime.)

Example 1:

Input: L = 6, R = 10 Output: 4 Explanation: 6 -> 110 (2 set bits, 2 is prime) 7 -> 111 (3 set bits, 3 is prime) 9 -> 1001 (2 set bits , 2 is prime) 10->1010 (2 set bits , 2 is prime) Example 2:

Input: L = 10, R = 15 Output: 5 Explanation: 10 -> 1010 (2 set bits, 2 is prime) 11 -> 1011 (3 set bits, 3 is prime) 12 -> 1100 (2 set bits, 2 is prime) 13 -> 1101 (3 set bits, 3 is prime) 14 -> 1110 (3 set bits, 3 is prime) 15 -> 1111 (4 set bits, 4 is not prime) Note:

L, R will be integers L <= R in the range [1, 10^6]. R - L will be at most 10000.

思考路程
1 首先获得 1 的个数,然后判断是不是质数。

868. Binary Gap

Given a positive integer N, find and return the longest distance between two consecutive 1's in the binary representation of N.

If there aren't two consecutive 1's, return 0.

Example 1:

Input: 22 Output: 2 Explanation: 22 in binary is 0b10110. In the binary representation of 22, there are three ones, and two consecutive pairs of 1's. The first consecutive pair of 1's have distance 2. The second consecutive pair of 1's have distance 1. The answer is the largest of these two distances, which is 2.

Example 2:

Input: 5 Output: 2 Explanation: 5 in binary is 0b101.

Example 3:

Input: 6 Output: 1 Explanation: 6 in binary is 0b110.

Example 4:

Input: 8 Output: 0 Explanation: 8 in binary is 0b1000. There aren't any consecutive pairs of 1's in the binary representation of 8, so we return 0.

Note:

1 <= N <= 10^9

思考路程
1 很简单,直接获取 1 的不同位置,一直循环找到最大的