这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
数组中数字出现的次数
剑指Offer 56 - I. 数组中数字出现的次数
难度:中等
一个整型数组nums里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(),空间复杂度是O()。
示例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 <= nuns.length <= 10000
题解
题目要求时间复杂度是O(),空间复杂度是O(),则不能通过暴力或者建表的方法去做。
位运算
在数组中判断两两元素是否相同,可以采用异或的方法。
对于数组中只出现一个不重复的元素,我们可以很轻松的通过全体异或的方式得到该值:a⊕a⊕b⊕b⊕c = 0⊕0⊕c = c
,代码如下:
// @params nums[]
var singleNumber = (nums) => {
let x = 0;
for (let num of nums) {
x ^= num;
}
return x;
}
console.log(singleNumber([2, 2, 4, 5, 4])) // 5
但是对于本题是不重复数字出现了两次,所以我们需要对其进行简化,步骤如下:
-
遍历nums执行异或,如
a⊕a⊕b⊕b⊕c⊕d = 0⊕0⊕c⊕d = c⊕d
。 -
若c⊕d某二进制位为1,则c和d的二进制位一定不同,我们可以根据二进制位1,将数组拆分成两个子数组,并且每个数组中只有一个不重复数字。例如:
对于[1,2,10,4,1,4,3,3],2和10是不重复数字,则
2 -> 0010;10 -> 1010; 2和10这两个二进制数中首位不同,2与10得到8 -> 1000,即通过8来划分两个子数组。
-
拆分nums为两个子数组。
-
分别遍历两个子数组执行异或,这就将问题解耦啦,将两个子数组异或后的值返回即可。
/**
* @param {number[]} nums
* @return {number[]}
*/
var singleNumbers = function (nums) {
let x = 0, // 子数组1不重复的数字
y = 0, // 子数组2不重复的数字
n = 0, // 用于划分数组
m = 1; // 用于划分数组
// 1. 遍历异或
for (let num of nums) {
n ^= num;
}
// 2.循环左移,计算出m
while ((n & m) === 0)
m <<= 1;
// 3.划分子数组 (num & m) === 1和(num & m) === 0,括号一定要加!
for (let num of nums) {
if ((num & m) !== 0) x ^= num; // 4.子数组异或
else y ^= num;
}
return [x, y]; // 返回结果
};
//console.log(singleNumbers([1,2,10,4,1,4,3,3]))
- 时间复杂度:O()
- 空间复杂度:O()
坚持每日一练!前端小萌新一枚,希望能点个赞
哇~