只出现一次的数字
YuArtian opened this issue · comments
只出现一次的数字
Round One !
请听题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?( 时间复杂度 O(n) ,并且空间复杂度为 O(1) )
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解法:
使用位操作,对所有元素做异或运算。
因为异或运算的性质是:任何一个数字异或它自己结果都是0
, 任何数字异或 0
结果都是它本身。
通过交换律 和 结合律 我们可以将 (如示例2) 变为如下过程: (1⊕1)⊕(2⊕2)⊕4
==> 0⊕0⊕4
==> 4
具体代码实现:
/**
* @param {number[]} nums
* @return {number[]}
*/
var singleNumber = function (nums) {
var res;
nums.forEach(function (v) {
res = res ^ v;
});
return res;
};
Roud Two !!
请听题
给定一个非空整数数组,除了某两个元素只出现一次以外,其余每个元素均出现两次。找出那两个只出现了一次的元素。(其余同上)
示例 :
输入: [1,2,1,3,2,4]
输出: [3,5]
解法:
依然是异或,依然像第一题一样
先对所有元素依次做异或,最后的结果就是那两个只出现一次的异或结果
示例过程如下:(1⊕1)⊕(2⊕2)⊕(3⊕4) ==> 3⊕4
, 得到结果 111
(二进制形式)。
那么接下来就是如何才能找到这两个只出现一次的元素。
因为任何一个数字异或它自己都等于 0,所以这个二进制数的每一个为 1 的位置其实都是我们要找的位置。
任意选取一个位置,将原数组分成两组,一组是在该位置全为 1 的数,另一组是在该位置全为 0 的数。
将这两组数分别做异或运算,得到的两个结果就是 两个只出现一次的数字。
/**
* @param {number[]} nums
* @return {number[]}
*/
var singleNumber = function(nums) {
let xor=0
// 所有元素做异或运算
for(let num of nums)xor ^= num
// 为了方便起见,我们找最后一位为 1 的位置
let lastOneBit=1
while(xor % 2===0){
xor /= 2
lastOneBit*=2
}
let res=[0,0]
for(let num of nums){
// js 里的 & 的意思就是:对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
// 所以可以通过 lastOneBit & num 来找到这个位置是 0 的所有元素
if((lastOneBit & num)===0){
res[0] ^= num
}else{
// 那个剩下的就是这个位置是 1 的所有元素
res[1] ^= num
}
}
return res
};
Round Three !!!
请听题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解法:
【笔记】网上大佬曾经说,如果能设计一个状态转换电路,使得一个数出现3次时能自动抵消为0,最后剩下的就是只出现1次的数。
开始设计:一个二进制位只能表示0或者1。也就是天生可以记录一个数出现了一次还是两次。
- x ^ 0 = x;
- x ^ x = 0;
要记录出现3次,需要两个二进制位。那么上面单独的x
就不行了。我们需要两个变量,每个变量取一位:
- ab ^ 00 = ab;
- ab ^ ab = 00;
这里,a
、b
都是32位的变量。我们使用a
的第k
位与b
的第k
位组合起来的两位二进制,表示当前位出现了几次。也就是,一个8
位的二进制x
就变成了16
位来表示。
- x = x[7] x[6] x[5] x[4] x[3] x[2] x[1] x[0]
- x = (a[7]b[7]) (a[6]b[6]) ... (a[1]b[1]) (a[0]b[0])
于是,就有了这一幕 ...
它是一个逻辑电路,a
、b
变量中,相同位置上,分别取出一位,负责完成00->01->10->00
,也就是开头的那句话,当数字出现3次时置零。
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let a = 0, b = 0;
for (let x of nums) {
b = (b ^ x) & ~a;
a = (a ^ x) & ~b;
}
return b;
};