原题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
思路
这道题目最简单的思路,是利用Hash查询的时间复杂度为O(1), 避免使用双层for循环,导致时间复杂度变为O(n^2)。但是这不是我们今天关注的重点。
因为,这两天我一直在看位运算符的相关内容, 其实本题可以在不使用额外的内存空间,并且是O(n)的时间复杂度下,解答出该题。只需要使用按位异或的操作符^
什么是按位异或^?
按位异或^, 本质上将两个操作数的二进制数的每一位对齐。然后按如下的规则取值,1 ^ 1 等于 0; 1 ^ 0 等于 1;0 ^ 1 等于1;0 ^ 0等于0。
举一个例子🌰。10和5之间进行按位异或操作的结果15。
0000 0000 0000 0000 0000 0000 0000 1010
// ^ XOR
0000 0000 0000 0000 0000 0000 0000 0101
// 等于 14=5s
0000 0000 0000 0000 0000 0000 0000 1111
✨除此之外,我们还需要知道两个准则:
- 两个相同的数进行按位异或等于0
- 任意一个数与0进行按位异或等于自身
// 0
100 ^ 100
// 0
-99 ^ -99
// 100
100 ^ 0
// -2
0 ^ -2
代码
解法1:利用hash
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let hashMap = new Map()
let result
for (let i = 0; i < nums.length; i++) {
if (!hashMap.has(nums[i])) {
hashMap.set(nums[i], 1)
} else {
hashMap.set(nums[i], 2)
}
}
for (let i = 0; i < nums.length; i++) {
if (hashMap.get(nums[i]) === 1) {
result = nums[i]
break
}
}
return result
};
解法2: 利用异或
没错,就是这么简单😂
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let result = 0
for (let i = 0; i < nums.length; i++) {
result = nums[i] ^ result
}
return result
};
🤔️ 为什么可以这样?原题中说给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。, 于是我们可以将代码执行的过程分解成下面的过程
-
nums = [a, b, a, c, b, c, d]
-
result = a ^ b ^ a ^ c ^ b ^ c ^ d
-
result = (a ^ a) ^ (b ^ b) ^ (c ^ c) ^ d
-
result = 0 ^ 0 ^ 0 ^ d
-
result = d 得到了只出现了一次的数字