首先你要明白的几个知识点:
(1)在计算机里面,负数是以补码存储的
(2)原码求补码:取反,+1
(3)补码求原码:取反,+1
(4)取反操作是在原码上进行的!
- 0s 表示一串 0,1s 表示一串 1。|:按位取或。
~
:按位取反。~
x 类似于 -x-1。如:~
-6: 5
-6的补码是+6(0000 0110)取反后再+1,为(1111 1001)+(0000 0001)=(1111 1010),
,也就是计算机中-6是用(1111 1010)来存储的,(1111 1010) 按位取反得到(0000 0101)这就是答案5
4的原码: 0000 0100,取反得到: 1111 1011, 观察符号,是负数,因为负数以补码存储的,所以问题转化为:
某个数x的补码是1111 1011,求x的值(由补码求原码) 取反: 0000 0100 ,+1: 0000 0101 = 5,
加上标点符号(负号) 得到结果: -5
```python
因为-5是负数,所以它是以5的补码表示的,所以转化为已知5的补码,求对应的原码,然后在取反.
5补码: 0000 0101,
取反: 1111 1010
+1: 1111 1011, 得到原码
取反: 0000 0100 = 4 ,加上标点负号(正号)得到结果:4
```python
x ^ 0s = x x & 0s = 0 x | 0s = x
x ^ 1s = ~x x & 1s = x x | 1s = 1s
x ^ x = 0 x & x = x x | x = x
- 利用 x ^ 1s = ~x 的特点,可以将一个数的位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。
- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
01011011 &
00111100
--------
00011000
- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。
01011011 |
00111100
--------
01111111
位与运算技巧
- n&(n-1) 去除 n 的位级表示中最低的那一位 1。例如对于二进制表示 01011011,减去 1 得到 01011010,这两个数相与得到 01011010。
01011011 &
01011010
--------
01011010
- n&(-n) 得到 n 的位级表示中最低的那一位 1。-n 得到 n 的反码加 1,也就是 -n=~n+1。例如对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。
10110100 &
01001100
--------
00000100
- n-(n&(-n)) 则可以去除 n 的位级表示中最低的那一位 1,和 n&(n-1) 效果一样。
移位运算
>> n
为算术右移,相当于除以 2n,例如 -7 >> 2 = -2。
11111111111111111111111111111001 >> 2
--------
11111111111111111111111111111110
>>> n
为无符号右移,左边会补上 0。例如 -7 >>> 2 = 1073741822。
11111111111111111111111111111001 >>> 2
--------
00111111111111111111111111111111
<< n
为算术左移,相当于乘以 2n。-7 << 2 = -28。
11111111111111111111111111111001 << 2
--------
11111111111111111111111111100100
mask 计算
- 要获取 111111111,将 0 取反即可,~0。
- 要得到只有第 i 位为 1 的 mask,将 1 向左移动 i-1 位即可,1<<(i-1) 。例如 1<<4 得到只有第 5 位为 1 的 mask :00010000。
- 要得到 1 到 i 位为 1 的 mask,(1<<i)-1 即可,例如将 (1<<4)-1 = 00010000-1 = 00001111。
- 要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 ~((1<<i)-1)。
- 统计两个数的二进制表示有多少位不同
class Solution(object):
def hammingDistance(self, x, y):
"""
:type x: int
:type y: int
:rtype: int
"""
return bin(x^y).count("1")
# (1^4)的结果是5,即0001^0100=0101=5。bin返回的是字符串,bin(10)返回'0b1010'
- 数组中唯一一个不重复的元素
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 任何数于0异或为任何数 0 ^ n => n
# 相同的数异或为0: n ^ n => 0
# 交换律:a ^ b ^ c <=> a ^ c ^ b
res=0
for num in nums:
res^= num
return res
- 找出数组中缺失的那个数
class Solution:
def missingNumber(self, nums: List[int]) -> int:
res = len(nums)
for idx, num in enumerate(nums):
res ^= idx ^ num
return res
- 数组中不重复的两个元素
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
# 参考官方题解
# difference between two numbers (x and y) which were seen only once
bitmask = 0
for num in nums:
bitmask ^= num
# rightmost 1-bit diff between x and y
diff = bitmask & (-bitmask)
x = 0
for num in nums:
# bitmask which will contain only x
if num & diff:
x ^= num # 这里是多次异或找到diff为1的位上只出现1次的数。
return [x, bitmask^x]
- 翻转一个数的比特位
class Solution:
def reverseBits(self, n: int) -> int:
res, power = 0, 31
while n:
res += (n & 1) << power # n&1能看最后一位是0还是1
n = n >> 1 # 最后一位不见了
power -= 1
return res
- 不用额外变量交换两个整数
a = a ^ b
b = a ^ b
a = a ^ b
- 判断一个数是不是 2 的 n 次方
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
return n > 0 and n & (n - 1) == 0
- 判断一个数是不是 4 的 n 次方
class Solution:
def isPowerOfFour(self, num: int) -> bool:
return num > 0 and num & (num - 1) == 0 and num % 3 == 1
9.7看完了
- 判断一个数的位级表示是否不会出现连续的 0 和 1
class Solution:
def hasAlternatingBits(self, n: int) -> bool:
return not ('11' in str(bin(n)) or '00' in str(bin(n)))
class Solution:
def hasAlternatingBits(self, n: int) -> bool:
tmp = n^(n>>1)
return tmp&(tmp+1) == 0
- 求一个数的补码
class Solution:
def findComplement(self, num: int) -> int:
# num和补数相加,就是满数位1的二进制数,即2**(n-1)-1
return 2**(len(bin(num))-2)-num-1
class Solution:
def findComplement(self, num: int) -> int:
count, n = 0, num
while n > 0:
count += 1
n = n >> 1
all_bits = pow(2, count) - 1
return num ^ all_bits
- 实现整数的加法
class Solution(object):
def getSum(self, a, b):
"""
:type a: int
:type b: int
:rtype: int
"""
# python整数类型为无限长整数类型.需要0x100000000取模,保证该数从32位开始到最高位都是0。其中0x表示16进制,8个0转为2进制就是32个0。
# a+b的问题拆分为(a和b的无进位结果) + (a和b的进位结果)
# 无进位加法使用异或运算
# 进位结果使用与运算和移位运算
# 循环此过程,直到进位为0
mask = 0x100000000
max_int = 0x7FFFFFFF # F:1111,7:0111。0x7FFFFFFF的二进制表示是首位是0,其余都是1。第一位是符号位,0 表示他是正数,就是说,这是最大的整型数int。
min_int = max_int + 1
while b != 0:
carry = (a & b) << 1 # 计算进位。
a = (a ^ b) % mask # 无进位加法,对0x100000000取模,保证该数从32位开始到最高位都是0。# 取余范围限制在 [0, 2^32-1] 范围内
b = carry % mask
return a if a <= max_int else ~((a % min_int) ^ max_int) # ~((a % MIN_INT) ^ MAX_INT) 其实可以简化为~(a ^ mask) 为什么要对负数做这样的处理呢?因为在python中int不是32位的,输出是64位,所以一个负数比如-2, 64位表示就是0x00000000FFFFFFFE, 用python求取这个16进制的值int('0x00000000FFFFFFFE', 16), 得到的数字是4294967294 不是我们想要的-2,所以: a^mask是先对a的前32位取反,对应-2,就得到0x0000000000000002 再用~操作符对所以位置取反,对应-2,得到0xFFFFFFFFFFFFFFFE总结一下就是,由于oj系统python最后返回的是64位,如果不对负数特殊处理,那么负数的前32位是0,最后输出的是大于32位的正数。
- 字符串数组最大乘积
class Solution:
def maxProduct(self, words: List[str]) -> int:
n = len(words)
masks = [0] * n
lens = [0] * n
bit_number = lambda ch : ord(ch) - ord('a')
for i in range(n):
bitmask = 0
for ch in words[i]:
# add bit number bit_number in bitmask
bitmask |= 1 << bit_number(ch)
masks[i] = bitmask
lens[i] = len(words[i])
max_val = 0
for i in range(n):
for j in range(i + 1, n):
if masks[i] & masks[j] == 0:
max_val = max(max_val, lens[i] * lens[j])
return max_val
- 统计从 0 ~ n 每个数的二进制表示中 1 的个数
class Solution(object):
def countBits(self, num):
"""
:type num: int
:rtype: List[int]
"""
res = [0]
for i in range(1, num + 1):
if i % 2 == 0:
res.append(res[i/2])
else:
res.append(res[i - 1] + 1)
return res