136. 只出现一次的数字
题目描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解法:异或
异或运算有以下三个性质。
- 任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a。
- 任何数和其自身做异或运算,结果是 0,即 a⊕a=0。
- 异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b。
var singleNumber = function(nums) {
let res = 0
for(let n of nums){
res = res ^ n
}
return res
};
剑指 Offer 56 - I. 数组中数字出现的次数
题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6] 输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3] 输出:[2,10] 或 [10,2]
- 限制:2 <= nums.length <= 10000
解法:异或
在人群找两条🐕 例如为335567最终要求出6,7
a.
先对所有数字进行一次异或,得到两个出现一次的数字的异或值result。result!=0。
根据得到的结果result,根据result找到67相互嫌弃的点在哪。
即在result中找到任意为 1的位。
6:1 1 0
7:1 1 1
b.
根据这个互相嫌弃的点,分成两组,对所有的数字进行遍历。
本着两家人不进一家门的原则,6,7就不会被分到一个组了。
成对出现的数字,会被它相同的数字领走。
例如第一个3先进了6这个组,当遍历第二个3时,这个第二个三就把第一个三领走了。他们最终会相遇,即他们抵消掉了。
就这样在每个组内进行异或操作,最终剩下6,7分在不同组里。
c.
总结:全程只有那俩不相同的数字互相嫌弃,最终他们会被分到不同的组。
var singleNumbers = function(nums) {
let ret = 0
//1.遍历 nums 执行异或:
/*设整型数组 nums=[a,a,b,b,...,x,y] ,
对 nums 中所有数字执行异或,
得到的结果为 x⊕y
*/
//
nums.forEach(n => ret ^= n)
//2.循环左移计算 flag
/*若x⊕y 某二进制位为 1 ,则 x 和 y 的此二进制位一定不同。
换言之
找到 x⊕y 某为 1 的二进制位,即可将数组nums 拆分为两个子数组。
*/
let flag = 1
while ((ret & flag) === 0) {
flag <<= 1
}
//拆分 nums 为两个子数组:
let a = 0, b = 0
for(let n of nums) {
if((n & flag) === 0){
//分别遍历两个子数组执行异或
a ^= n
} else {
b ^= n
}
}
return [a, b]
};
解法:map
var singleNumbers = function(nums) {
const map = new Map()
for(let n of nums){
if(map.has(n)){
map.set(n, map.get(n)+1)
}else{
map.set(n, 1)
}
}
let res = []
for(let key of map.keys()){
if(map.get(key) === 1){
res.push(key)
}
}
return res
};