解法一,哈希表。时间复杂度,空间复杂度。
var singleNumber = function(nums) {
let num_cnt = new Map()
for (let num of nums) {
if (num_cnt.has(num)) num_cnt.set(num, num_cnt.get(num)+1)
else num_cnt.set(num, 1)
}
for (let num of num_cnt.keys()) {
if (num_cnt.get(num) === 1) return num
}
};
解法二,位运算。将所有数看看作是32位的二进制数,对于每一位,计算该位上1有多少个,如果刚好是3的倍数个,说明要找的数该位上为0,否则为1.由于每个数视作32位,那么时间复杂度,空间复杂度。
var singleNumber = function(nums) {
let res = 0
for (let i = 0; i < 32; i++) {
let cnt = 0
for (let num of nums) cnt += num&(1<<i)?1:0
res += (cnt%3)<<i
}
return res
};
解法三,基于有限状态自动机的位运算,参考了 只出现一次的数字 II(有限状态自动机 + 位运算,清晰图解).大致思路如下:对于每一位,一开始是0,每碰到一个1就++,当加到3的时候就归零。于是我们用两位two和one来表示,00->01->10->00.具体思路可以看原文章。这种方法难点在于如何通过真值表构建运算关系。
var singleNumber = function(nums) {
let ones = 0, twos = 0
for (const num of nums) {
ones = ones^num&~twos
twos = twos^num&~ones
}
return ones
};
再来看两道相似题。
第一题136. Single Number,比较简单。
var singleNumber = function(nums) {
return nums.reduce((prev,next)=>prev^next, 0)
};
第二题260. Single Number III,这题我们先对所有数进行异或得到sum,sum=a^b,由于a!=b,那么sum肯定不为0,说明sum的二进制表示中至少有一位为1,即a和b在这一位上一个为0一个为1.我们根据这一位将所有数分成两拨,一拨是这一位上为0的数,另一拨是这一位上为1的数,a和b分别在这两拨中。现在每一拨都是上面一题,答案呼之欲出。
var singleNumber = function(nums) {
let sum = nums.reduce((prev,next)=>prev^next, 0)
for (let i = 0; i < 32; i++) {
if (sum&(1<<i)) {
let a = 0, b = 0
for (let num of nums) {
if (num&(1<<i)) a ^= num
else b ^= num
}
return [a,b]
}
}
};