题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素
。
要求:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
例子1:
输入: [2,2,1]
输出: 1
例子2:
输入: [4,1,2,1,2]
输出: 4
刚开始看到这个题目感觉很简单,脑海中想到了用hashMap做个累加,然后找到map中value=1的key即为题解,又或者采用集合List来做记录,不存在添加,存在删除。
但是题目要求的是不要用额外的内存空间来实现,发现两个方案并不行。因为需要额外的内存开销,所以不满足题目,之后又想了很多的方式去来实现,不过都需要额外的内存开销而失败,最后看了下官方答案,瞬间感觉自己真的是很笨。
官方的解决方案:因为数组只有一个元素出现一次以外,其他元素都是出现两次,什么算法可以把两个相同的元素变成0呢?异或(eor)位运算
异或位元算:
在计算机中普遍运用,异或(eor)的逻辑符号一般用eor,也有用⊕的:
真⊕假=真
假⊕真=真
假⊕假=假
真⊕真=假
或者为:
True ⊕ False = True
False ⊕ True = True
False⊕ False = False
True ⊕ True = False
部分计算机语言用1表示真,用0表示假,所以两个字节按位异或如下
异或运算的规则是
0⊕0=0,0⊕1=1
1⊕0=1,1⊕1=0
1. 归零律:a ⊕ a = 0
2. 恒等律:a ⊕ 0 = a
3. 交换律:a ⊕ b = b ⊕ a
4. 结合律:a ⊕ b ⊕ c = a ⊕ ( b ⊕ c ) = ( a ⊕ b ) ⊕ c
5. 自反:a ⊕ b ⊕ a = a
6. d = a ⊕ b ⊕ c 可以推出 a = d ⊕ b ⊕ c.
7.若x是二进制数0101,y是二进制数1011;
则x⊕y=1110
只有在两个比较的位不同时其结果是1,否则结果为0
即“两个输入相同时为0,不同则为1”!
归零律:相同元素做异或运算结果是0
恒等律:任意元素异或0等于本身
交换律:两个不能元素异或运算不会因为顺序的不一致导致结果不同
结合律:多个元素在做异或运算时顺序随意调整不会导致结果不同
自反律:两个相同元素与任意一个元素X做异或运算结果就是X。
所以最终的结论可以得出:
上述题目只要做连续的异或运算最后得到的就是只出现过一次数字的元素,因为其他元素出现两次都变成了0,即上述恒等律,任意数与0做异或运算等于本身。
代码结果如下:
class Solution {
public int singleNumber(int[] nums) {
for(int i=1; i<nums.length; i++) {
nums[0] ^= nums[i];
}
return nums[0];
}
}
做题总结:看来学好数学很重要,一行代码解决的问题,我可能需要5行才能解决,还不是理想答案,刚开始学习算法,争取早日追上大部队。
最后分享一句很喜欢的话: