异或,就是无进位的加法,相同为零,相异为一。其计算机符号为“XOR”。
特点:
(1) 一个数和 0 做 异或 运算等于本身:a^0 = a
(2) 一个数和 其本身 做 异或 运算等于 0:a^a = 0
(3) 异或 运算满足交换律和结合律:a^b^a = (a^a)^b = 0^b = b
常用操作:
1. 使某些特定的位翻转
对数10100001的第2位和第3位翻转,则可以将该数与00000110进行按位异或运算。
10100001^00000110 = 10100111
2. 实现两个值的交换,而不必使用临时变量。
a = a ^ b
b = a ^ b
a = a ^ b
执行这三行语句之后便可实现a,b的值互换。但有一点需注意,不可对自己进行异或运算,否则会导致抹掉数值。
一个小技巧: 提取二进制数值R最右侧1的位
rightone = R & ( ~R + 1 )
设定 R = 01010110
则 ~R = 10101001
~R + 1 = 10101010
R & ( ~R + 1 ) = (01010110) & (10101010) = 00000010 即 第2位是R最右侧值为1的位
应用:位运算+异或
有一个数组Arr ,里面的值都是整型类型,用时间复杂度为n,空间复杂度为1的算法实现:
1)已知只有一个数组出现了奇数次,其他所有的数字都出现了偶数次。请找到这个出现奇数次的数字。
利用位运算异或的特点,任何数和自己异或的结果都为0、任何数和0异或的结果都是它自身。
对数组Arr进行遍历,把所有元素进行异或,结合交换律计算,出现偶数次的数字进行异或的结果都是0,最终的结果就是0和出现奇数次的数字进行异或,结果是这个数字本身。
def get_1_num(Array):
result = 0
for ele in Array:
result = result ^ ele
print 'The num is: ', result
2)已知有两个数字出现了奇数次,其他所有的数字都出现了偶数次。请找到出现了奇数次的这两个数字。
承接第一问的思路,对Arr的元素遍历进行异或,最终结果是出现了奇数次的这两个数字异或的 结果。那么接下来就是如何获取这两个数字的具体值了。
假设这两个出现奇数次的数字分别为a,b,这两个数字做异或结果不为0,说明它们有一位上的值是不同的;另外出现了偶数次的数字在该位上的值要么和a相同,要么和b相同。
下面我们就提取a^b的结果值最右侧的1的位rightone,以此来把Arr里的元素进行分类:以a和b为代表的该位上数字值为0 或 1的两类。
上面讲到,提取二进制数值R最右侧1的值的计算方法:
rightone = R & ( ~R + 1 ) ,这里R = a ^ b = result
接下来,继续遍历Arr数组,将元素依次和rightone进行异或,并将符合与rightone异或值为0(该指定位与rightone相同) 或者值为1(该指定位与rightone不同)的元素再单独进行异或,得到最终结果,记为result_new。这一步的主要目的就是将上面分出的两类数据取其一再次进行异或,最终的结果就是只出现奇数次的那个数字的值。
def get_2_num(Array):
result = 0
for ele in Array:
result = result ^ ele
rightone = result & ( ~result + 1 )
result_new = 0
for ele_new in Array:
if (rightone ^ ele_new == 0): #或者rightone ^ ele_new == 1
result_new = result_new ^ ele_new
print 'The one of nums is :{},and another one is : {}'.format(result_new,result^result_new)