文章目录
前言
通常来讲,我们说异或问题就是:相同为0,不同为1
左神说,我们可以将异或问题看为无进位相加
举个例子:
6^7
6 的二进制表示为110,7的二进制表示为111,通过“相同为0,不同为1”的口诀得到001。
用无进位相加我们可以得到,0+1=1,1+1=2写0,此时不进位,然后1+1=2写0,此时不进位,也可以得到001。
一、异或运算的性质
1. 0^N == N , N ^ N == 0
2. 异或运算满足交换律和结合律
根据第二条性质可知,不管顺序,位置,同一批数异或运算结果一定是一样的。
二、练习题
1.不用额外变量交换两个数
class XOR():
# 不用额外变量交换两个数
def swap(self, a, b):
a = a ^ b
b = a ^ b
a = a ^ b
# 代码解释:
假设a=6,b=5
第一步:a = a ^ b 也就是 a = 6 ^ 5
第二步:b = a ^ b 也就是 b = 6 ^ 5 ^ 5 ,
根据性质一跟性质二可知,b = 6 ^ 5 ^ 5 = 6 ^ 0 = 6
第三步:a = a ^ b 也就是 a = 6 ^ 5 ^ 6 = 6 ^ 6 ^ 5 = 0 ^ 5 = 5
# 存在的问题:
假如 a=6,b=6。此时进行异或操作,会不会根据性质一的N ^ N == 0 ???
注意,不会根据性质一这样的。
因为 a与b虽然值相同,但是他们是两个变量,a与b在计算机中各占一块内存。
假如,a与b指向同一个,那么此时就会出错。
2.一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
class xor():
# 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
def xor(self, arr):
n = len(arr)
eor = 0
for i in range(n):
eor ^= arr[i]
return eor
3.怎样把一个int类型的数,提取出最右侧的1来
假设n
则结果为 n&((~n)+1)
4.一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
推理过程:假设数组中这两种数为a和b,那么我对数组全部进行异或运算,得到的eor=a ^ b,一定如此。又因为a和b是两种数,那么eor=(a ^ b) != 0。那么eor一定存在某一位上不是0而是1。假设eor的第八位为1,那么我们可以将整个数组分为两个部分,即第八位上=0和第八位上=1的,并且a和b 一定是分属于两个部分的。此时,我想只异或第八位上=1的那部分,用eor’表示,那么eor’一定是a或者b,具体是谁不知道,假如是a那么如何去求b呢??那就是eor^eor’。这里第八位为1只是一个例子,那么找哪个呢??就找最右侧为1,根据3来求,知道了最右侧的1,就可以根据推理过程求出问题。
class xor():
# 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
def xor(self, arr):
n = len(arr)
eor = 0
for i in range(n):
eor ^= arr[i]
# eor = a ^ b
# eor != 0
# eor 必然有一个位置上是1
rightone = eor & (~eor + 1) # 提取最右侧为1
onlyone = 0
for i in range(n):
if rightone & arr[i] != 0:
onlyone ^= arr[i]
otherone = onlyone ^ eor
return onlyone, otherone
5.输出二进制1的个数
# 1.暴力法,遍历一个一个的数,for
# 2.用异或
class xor():
# 输出二进制1的个数
def xor(self, n):
count = 0
while n != 0:
rightone = n & ((~n)+1) # 每次先提取最右侧的1
count += 1 # 提取一个计数器加一个
n ^= rightone # 提取完就将最右侧的1抹掉
return count