类型题Ⅱ:位运算
类型题Ⅱ:位运算
文章目录
位运算 —— 异或
英文: exclusive OR,缩写 xor
数学符号:
⊕
⊕
⊕ **运算法则:**
a
⊕
b
=
(
¬
a
∧
b
)
∨
(
a
∧
¬
b
)
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
a⊕b=(¬a∧b)∨(a∧¬b)
口诀: “相同为 0,相异为 1”——XOR 在英文里面的定义为:either one (is one), but not both, 即只有一个为真(1)时结果取真(1)
真值表:
A | B | ⊕ ⊕ ⊕ |
---|---|---|
F | F | F |
F | T | T |
T | F | T |
T | T | T |
性质:
- 交换律:A ^ B = B ^ A- 结合律:A ^ (B ^ C) = (A ^ B) ^ C- 恒等律:X ^ 0 = X,即任何数与 0 的异或都等于该数本身- 归零律:X ^ X = 0- 自反:A ^ B ^ B = A ^ 0 = A- 对于任意的 X: X ^ (-1) = ~X- 若 A ^ B = C,则 B ^ C = A # 相关题目
集合 S 包含从1到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复。
给定一个数组 nums 代表了集合 S 发生错误后的结果。你的任务是首先寻找到重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
位运算解法:根据异或性质,用 1~n-1
的异或结果与错误数组做异或,得到的结果就是缺失的数字。
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
mask = xor0 = xor1 = 0
for idx, num in enumerate(nums, 1):
mask ^= idx ^ num
bitmask = mask & -mask
for idx, num in enumerate(nums, 1):
if idx & bitmask:
xor1 ^= idx
if num & bitmask:
xor1 ^= num
xor0 = mask ^ xor1
for num in nums:
if num == xor0:
return [xor0, xor1]
return [xor1, xor0]
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
位运算解法:由于异或运算满足交换律和结合律,即KaTeX parse error: Double superscript at position 7: a ^ b ^̲ a = b ^ a ^ a …,所以对整个数组异或,就能将相同的数字抵消掉,最后只剩下单独的一个数字,即为要找的答案。
时间复杂度:
O
(
n
)
O(n)
O(n),对长度为 n 的数组遍历一次<br> 空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return reduce(lambda x,y: x ^ y, nums)
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
位运算解法:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ones, twos = 0, 0
for num in nums:
ones = ones ^ num & ~twos
twos = twos ^ num & ~ones
return ones
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
位运算解法:在136. 只出现一次的数字的基础上,这里单独的是两个数字,考虑将原数组分成两组,将两个单独的数字分配到两个数组中,这样再按照 136 题的解法,分别做异或即可得到答案。
问题在于怎么恰好把两个单独的数字分到两组?由于所有数组异或的结果就是两个单独数字的异或结果,而结果的每一位为 0 或 1。如果是 1 表示该位不同,那么就可以任选一个为 1 的位置,按照该位置对原数组分割,如果为 0 就归第一组,否则归第二组。这样就能保证两个单独的数字被分到不同的组,同时相同的数字对儿能够被分到同一组。
在实际选择中,可以选择不为0的最低位进行分组。
解题过程:
- 先对所有数据进行异或,得到两个只出现一次的数字的异或值- 在该异或值中找到任意为 1 的位置- 根据该位对所有数字进行分组- 每个分组进行异或,分别得到一个单独的数
时间复杂度:O(n),遍历数组两次
空间复杂度:O(1)
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
ret = reduce(lambda x, y: x ^ y, nums) # 整个数组异或 = 两个单独数的异或
div = 1
while div & ret == 0:
div <<= 1 # 从右向左找第一个不为0的位置
a, b = 0, 0
for n in nums:
if n & div:
a ^= n
else:
b ^= n
return [a, b]
剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
这道题和前面的137. 只出现一次的数字 II一样。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
ones, twos = 0, 0
for num in nums:
ones = ones ^ num & ~twos
twos = twos ^ num & ~ones
return ones
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
class Solution:
def add(self, a: int, b: int) -> int:
x = 0xffffffff
a, b = a & x, b & x
while b != 0:
a, b = (a ^ b), (a & b) << 1 & x
return a if a <= 0x7fffffff else ~(a ^ x)
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
# 递归
class Solution:
def __init__(self):
self.res = 0
def sumNums(self, n: int) -> int:
n > 1 and self.sumNums(n-1) # 不用if
self.res += n
return self.res