位运算的特点:
1.效率高,内存消耗少
2.在某些情况中,位操作可以避免或者减少在一个数据结构上需要进行循环的次数,并且可以成倍的效率提升,
因为位操作是并行处理的。但是位操作的代码比较难以编写和维护。
3.所谓按位运算就是指先将数字转化成二进制数字,然后按照二进制的规则进行相关的运算。
位运算常用的运算符
&(按位与): 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
例如:
0 0 1 1 1 0 1 1
1 0 1 1 0 1 0 1
--------------------
0 0 1 1 0 0 0 1
| (按位或): 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。
例如:
0 1 0 1 0 0 1 1
1 0 0 1 1 0 0 1
-------------------
1 1 0 1 1 0 1 1
~(按位非): 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1
例如:
1 1 0 0 1 0 1 0
------------------
0 0 1 1 0 1 0 1
^(按位异或): 按位异或运算符:当两对应的二进位相异时,结果为1
例如:
0 0 1 1 0 1 0 1
0 1 0 1 0 1 0 0
------------------
0 1 1 0 0 0 0 1
<< (有符号左移位) : 左移动运算符:运算数的各二进位全部左移若干位,由"<
例如: << 1 print(6 << 1) # 6 * 2**1
0 0 1 0 1 0 1 1
------------------
0 1 0 1 0 1 1 0
>>(有符号右移位): 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数
例如: >> 1 print(6 >> 1) # 6 // 2**1
0 1 0 1 0 1 0 1
------------------
0 0 1 0 1 0 1 0
x & (x - 1) 方法解释:
x & (x - 1)的作用是将x的二进制表示中右边第一个1置0,我们来分步看一下为什么会这样子。首先x-1的作用是将x最右边的1置0,
并且这个1后面的所有0置1(如果这个1已经是最后一位则忽略不计)。如果将减一后的结果和原数按位相与,首先最右边一个1由于
变成0了,肯定与的结果是0,最右边1后面的数字,原数都是0,现在都是1,那么与的结果肯定也还都是0。比如现在一个二进制数是
x: 0010,0101,0000
x−1: 0010,0100,1111
x&(x−1): 0010,0100,0000
这里就没有考虑负数的情况了,由于计算机中负数是补码形式存储,而Python中整数的位数又没有限制,感觉没啥意义,这里就pass了;
有哪种题型适合用位运算符:
例: 1.只出现一次的数字:
题意说明:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解题思路:
1.利用异或的性质: 两个相同的数异或后则为0,0和一个数异或后得到那个数的本身
2.根据题意,除了某个元素只出现一次以外,其余每个元素均出现两次。显然将所有数字异或后,得到的结果即为出现一次的值
代码如下:
from typing import List
class Solution:
def singleNumber(self, nums: List[int]) -> int:
res = 0
for i in nums:
res = res ^ i
return res
if __name__ == '__main__':
s = Solution()
print(s.singleNumber([2,2,1]))
例: 2.位1的个数:
题意说明:
编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
进阶:
如果多次调用这个函数,你将如何优化你的算法?
解题思路:
1.根据题意可以利用位运算符 & 利用 n&(n-1)能够消灭 n 中最右侧的一个 1
2.定义一个计数变量
3.当 数值 n 不等于 0 时,利用 n&(n-1) 方法记录一共执行了几次就可以判断一共有多少个1
代码如下:
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n != 0:
n = n & (n - 1)
count += 1
return count
if __name__ == '__main__':
a = Solution()
(a.hammingWeight(10000111111111111101))
例: 3.汉明距离:
题意说明:
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
注意:
0 ≤ x, y < 231.
示例:
输入: x = 1, y = 4
输出: 2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
解题思路:
1.利用位运算符的 异或的性质: 两个相同的数异或后则为0,0和一个数异或后得到那个数的本身
2.题意汉明距离指的是这两个数字对应二进制位不同的位置的数目,所以就是两个数异或为 1 的个数
3.定义一个计数变量
4.当 数值 a 不等于 0 时,利用 a&(a-1) 方法记录一共执行了几次就可以判断一共有多少个1
代码如下:
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
a = x ^ y
count = 0
while a != 0: # 汉明重量
a = a & (a - 1)
count += 1
return count
if __name__ == '__main__':
s = Solution()
print(s.hammingDistance(1,4))
例: 4.汉明距离总和:
题意说明:
两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。
计算一个数组中,任意两个数之间汉明距离的总和。
示例:
输入: 4, 14, 2
输出: 6
解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010。(这样表示是为了体现后四位之间关系)
所以答案为:
HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6.
注意:
数组中元素的范围为从 0到 10^9。
数组的长度不超过 10^4。
解题思路1:
1.根据题意第一想法就是遍历数组,进行两个 for 循环将所有组合都求出来
2.在利用位运算符的 异或的性质: 两个相同的数异或后则为0,0和一个数异或后得到那个数的本身
3.题意汉明距离指的是这两个数字对应二进制位不同的位置的数目,所以就是两个数异或为 1 的个数
4.定义一个计数变量
5.当 数值 a 不等于 0 时,利用 a&(a-1) 方法记录一共执行了几次就可以判断一共有多少个1
注意: 但是这样就三层循环 时间复杂复杂度达到O(n²),会超出时间限制,所以要用第二种方法
代码如下:
from typing import List
class Solution:
def totalHammingDistance(self, nums: List[int]) -> int:
count = 0
for i in range(len(nums)):
for j in range(i+1,len(nums)):
a = nums[i] ^ nums[j]
while a != 0:
a = a & (a - 1)
count += 1
return count
if __name__ == '__main__':
s = Solution()
print(s.totalHammingDistance([4, 14, 2]))
解题思路2:
1.这里我们转变思维,从逐一数字的横向比较,转变为纵向的逐位比较,来优化时间复杂度
2.对于每一个数的某一位来说,若当前位为 1 ,那么对于当前位来说,所有数字的同位上为 0 的个数,即当前数字的当前位的汉明距离
3.因此,每一位汉明距离为 0 出现的次数乘以 1 出现的次数,最后遍历 32 位即可
代码如下:
class Solution:
def totalHammingDistance(self, nums: List[int]) -> int:
res = 0
for i in range(32): # O(32)
count_0 = 0
count_1 = 0
for j in range(len(nums)):
if (nums[j] >> i) & 1 is not 0: # 每个数右数第 i 位的汉明距离
count_1 += 1 # 第 j 个数右移 i 位,和 1 做位与运算,只关注最后一位
else:
count_0 += 1
res += count_0 * count_1
return res
if __name__ == '__main__':
s = Solution()
print(s.totalHammingDistance([4, 14, 2]))