异或题总结
1.概念
a、类似于无进位相加
b、0 ^ N = N
c、N ^ N = 0
d、满足交换律和结合律
2.经典性质题目
1⃣️、如何不用额外变量交换两个数
a = a ^ b
b = a ^ b (a ^ b ^ b == a)
a = a ^ b (a ^ b ^ a == b)
# 需要注意a和b可以一样 但是不能指向同一块内存 否则该内存会被刷成0 比如一个数组交换的时候
a = [1, 3, 100]
i = 0
j = 0
(0)a[i] = a[i] ^ a[j] (a[0] = 0)
(0)a[j] = a[i] ^ a[j]
(0)a[i] = a[i] ^ a[j]
2⃣️、一个数组中有一种数出现奇数次,另一种出现偶数次,怎么找到
# 遍历一遍 不断将取出来的数加入异或 这样偶数次的变成了0 奇数的还是本身
a = [1, 1, 1, 2, 2]
In [19]: eor = 0
In [20]: for i in a:
...: eor ^= i
...:
In [21]: eor
Out[21]: 1
3⃣️、将一个int类型的数,提取出最右侧的1来
# a: 01101110010000
#-a: 10010001110000
b = a & (-a)
= a & (~a + 1)
# b: 00000000010000
In [22]: test = 7
In [23]: ~test + 1
Out[23]: -7
4⃣️、一个数组中有两种数出现奇数次,其他出现偶数次,怎么找到这两种数
# 遍历一遍,不断作异或,最终结果eor = a ^ b, 且a != b,eor必不为0
# a ^ b的结果的某一位若为1则代表a和b在这一位上不一样
# 找到eor的其中一个1的位置,借此区分a和b eor'只异或此位为1的避开另一个数,由此得到其中一个数,另一个数可由eor异或已经得到的数获得
In [24]: a = [1, 1, 2, 2, 3, 5, 5, 5]
In [30]: eor = 0
In [31]: for i in a:
...: eor ^= i
In [32]: flag = eor & (-eor)
In [33]: for i in a:
...: if i & flag != 0:
...: eor_ ^= i
In [34]: print(eor_, eor ^ eor_)
5 3
5⃣️、一个数组中有一种数出现k次,其他数都出现m次,m > 1,k < m,找到出现k次的数, 要求额外空间复杂度O(1),时间复杂度O(n)
# 开一个大小为32的数组 统计每位出现了几次1, 然后遍历每一位,如果%m不为0,则代表这一位上出现k次的数位1,得到最终结果
In [35]: a = [0] * 32
In [36]: arr = [1, 1, 1, 3, 3, 3, 4, 4]
In [39]: for i in arr:
...: for num in range(32):
...: a[num] += (i >> num) & 1
...:
In [40]: ans = 0
In [42]: m = 3
In [43]: k = 2
In [44]: for i, x in enumerate(a):
...: if x % m == 0:
...: continue
...: if x % m == k:
...: ans |= (1 << i) # ans 不能初始化为-1 否则异或不能给值了
else:
ans = -1
...:# 如果数组里有0 需要判断0是不是最终结果 统计0的数量 如果不是 应该返回-1 找不到答案
if ans == 0:
count = 0
for(x : a):
if(x == 0):
count ++
if count != k:
ans = -1
In [45]: ans
Out[45]: 4