LeetCode上的136编号的原题
知识储备:
异或 ^ 运算(同为0,异为1,可理解为无进位相加)
其满足交换结合律
A^B=B^A (A^B)^C=A^(B^C)
0^N=N 0^0=0 1^1=1
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
// 同为0,异为1
let res = 0;
for(let i=0;i<nums.length;i++){
res = res^nums[i];
}
return res;
};
进阶版: 有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。
解析: 根据前面找一个不同数的思路算法,在这里把所有元素都异或,那么得到的结果就是那两个只出现一次的元素异或的结果。
然后,因为这两个只出现一次的元素一定是不相同的,所以这两个元素的二进制形式肯定至少有某一位是不同的,即一个为 0 ,另一个为 1 ,现在需要找到这一位。
根据异或的性质 任何一个数字异或它自己都等于 0,得到这个数字二进制形式中任意一个为 1 的位都是我们要找的那一位。
再然后,以这一位是 1 还是 0 为标准,将数组的 n 个元素分成两部分。
- 将这一位为 0 的所有元素做异或,得出的数就是只出现一次的数中的一个
- 将这一位为 1 的所有元素做异或,得出的数就是只出现一次的数中的另一个。
/**
* @param {number[]} nums
* @return {a,b}
*/
var singleNumber = function(nums) {
// 同为0,异为1
let eor = 0;
for(let i=0;i<nums.length;i++){
eor = res^nums[i];
}
//eor = a^b
//eor != 0
//eor 的数字二进制一定有一位是1,那样说明a,b在这一位上一个为0,一个为1
let rightOne = eor & (~eor + 1) //取反加1 与本身与 提取出最右边的1
let onlyOne = 0;
for(let cur=0;i<nums.length;i++){
if(nums[cur] & rightOne == 0){
onlyOne ^= nums[cur];
}
eor = res^nums[i];
}
return {onlyOne,eor^onlyOne};
};