异或位运算的妙用
这是一道力扣的简单题,最开始我想的是用数组,先对所有数据进行排序,然后遍历数组元素,如果a[i] = a[i+1],那么i+=2;continue;因为只有一个数字出现了一次,那么当a[i] != a[i+1]时,a[i]就是要找的数。
后面看了评论区之后,发现一种很简洁有意思的做法——按位异或
一、什么是异或?
之前离散数学里学习到这样一种特别的逻辑运算:按位异或。计算机进行按*异或运算的步骤为:
1、将两个数a、b先转换为二进制数(此步就决定了后续异或运算中每一次对应位的异或运算都只有0或1参与)
2、两个二进制数对应位进行异或运算,其规则是:如果参与异或运算的两个数相同,则该位的异或结果为0;否则,结果为1。
3、当所有位的异或运算都结束后,计算机再将得到的新二进制数转换为与a、b的进制相同的数输出,作为a^b的结果。
注:C语言里用符号“^”作为异或运算符,文章中的异或运算符也用这个表示
异或运算遵守的运算法则
- 归零律:a ^ a = 0
- 恒等律:a ^ 0 = a
- 交换律: a ^ b = b ^ a
- 结合律:a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c)
- 自反:a ^ b ^ a = b (可使用交换律得到)
二、异或运算的实例以及一些说明
看再多理论方法,如果不去实战体验,那就相当于没学
1、先用一个简单例子表现一下上面所说的异或运算步骤
以十进制数 5 和 3 进行异或运算为例:
第一步:计算机将二者转换为二进制数
5(十进制) --> 0101(二进制), 3(十进制) --> 0011(二进制)
第二步:对应位进行异或运算
0 1 0 1
0 0 1 1
---------- (对应位进行异或运算,相同的数字异或,结果为0;不同的数字异或,结果为1)
结果:0 1 1 0
运算结果就是二进制数0110
第三步:将得到的二进制数转换为和参与运算的5和3一样的十进制数输出得到5异或3的结果
5 ^ 3 = 6(C语言中用^表示异或运算符)
2、多个数据进行异或,当相同的数没挨在一起时,运算顺序
由于异或运算是满足交换律和结合律的,所以你大可认为在进行运算的时候,所有的数都会先进行交换,将相同的数放在一起,然后使用结合律先对相同的数进行异或运算,再进行后面的运算
举个例子:
a ^ b ^ c ^ a ^ d ^ c ^ d
=a ^ a ^ b ^ d ^ d ^ c
=0 ^ b ^ 0 ^ c
=0 ^ 0 ^ b ^ c
=0 ^ b ^ c
然后再根据b、c的具体值进行之后的运算
三、题目的异或解法
有了以上对异或运算的理解后,就可以很容易使用异或的方法解决这道题了
代码如下
int singleNumber(int* nums, int numsSize)
{
int b;
b = nums[0];
int i; b;
for(i = 1; i < numsSize; i++)
{
b = b ^ nums[i];//相同的数之间异或为0,0异或a为a
}
return b;
}
四、异或的其他使用
1、不使用额外空间完成两个数的交换
借助异或在原来两个数的空间里直接对两个数进行修改,从而不必声明一个新的变量,来开辟一个新的空间做中转站,进而节省了内存
int a = 10, b = 5;
a = a ^ b;
b = a ^ b;
//上面这一步值得注意的是:这一步的a相当于10 ^ 5
//所以这一步的a ^ b实际上是:10 ^ 5 ^ 5
a = a ^ b;
//由上面第二步可知,b已经经过异或运算变成了10,所以这一步相当于:10 ^ 5 ^ 10
暂时记录到这里,以后有什么新的想法再写进来。如果有什么错误的地方,欢迎指出