今天在学刷力扣的题的时候遇到一个很有意思的题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
刚开始拿到这个题的时候,我的想法其实暴力破解,还是想要用利用一个时间复杂度较好的排序算法先排序,然后在进行遍历查找那一个元素,但是显然,在一般情况下的排序算法最好的时间复杂度也是nlogn,所以我就陷入困惑。。。
但是在看了大神的代码后,突然回忆起来曾经学过的位运算,也许能解决这道题目。
首先抬出与本题相关的位运算的基本公式:
- 任何数与自身进行异或运算,等于0。如1 ^ 1 = 0,5 ^ 5 = 0(101 ^ 101 = 0)
- 任何数与0进行异或运算,等于其本身。如0 ^ 1 = 1,5 ^ 0 = 5,(101^000 = 101)
- 异或运算具有交换律:a ^ b ^ c = a ^ c ^ b,如5 ^1 ^5 = 5 ^ 5 ^ 1 = 0 ^ 1 = 1)
有了上述的公式可以很好地得到最后只出现一次的元素,具体做法如下。
- 第一步首先用0与数组的第一个数字异或运算,得到本身(这一步其实也是为了使循环里的操作都是一样的)
- 每次循环都使当前数组的值与之前的值进行异或
- 重复第二步,直至数组元素结束
- 根据交换律可以得到最后只出现的一次的值
例如:{1,3,3,1,6,6,5}(为了方便看懂,我把十进制数字转换为对应的二进制)
第一步: 000 ^ 001 = 001
第二步: 001 ^ 011 = 010
第三步: 010 ^ 011 = 001
第四步: 001 ^ 001 = 000
第五步: 000 ^ 110 = 110
第六步: 110 ^ 110 = 000
第七步: 000 ^ 101 = 101
所以最后的答案就是101!
最后附上我使用C语言的代码!
int singleNumber(int* nums, int numsSize){
int i;
int k = 0;
for(i = 0;i<numsSize;i++)
{
k = k^nums[i];
}
return k;
}
也算是今天学习的感悟,才开始刷题,感觉很有意思!希望以后也能多多收获知识!