「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」。
题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例1
输入: [2,2,1]
输出: 1
示例2
输入: [4,1,2,1,2]
输出: 4
思路
如果不考虑时间和空间复杂度,首先我们可以统计 nums
中每个数字出现的次数,然后返回出现次数等于1的那个,即为答案;
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let map = {};
// 统计次数
for(let num of nums) {
if(!map[num]) map[num] = 0;
map[num]++;
}
// 寻找次数为1的,并返回
for(let num in map) {
if(map[num] == 1) return num;
}
};
思考: 上述的解法时间、空间复杂度都为 O(n)
,且在时间复杂度上,进行了两次 O(n)
遍历,因为在后面还进行了一个寻找次数为1的元素 上
为了简化后面寻找次数为1的元素所花费的时间,在存储次数的时候,可以选择使用 Set
结构,遍历时,判断 Set
中是否存在该元素,不存在则放入 Set
中,存在则删除该元素;最后 Set
中就会只剩下一个元素,即为题目所求;
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let set = new Set();
for(let num of nums) {
if(!set.has(num)) {
set.add(num);
} else {
set.delete(num);
}
}
// 转换为 Array结构
set = [...set];
return set[0]
};
再思考: 上面两种算法的空间复杂度都为 O(n)
,不满足题目要求不使用额外空间的条件。
这里如果想到满足题目要求,就要用到位运算的 异或
操作了,因为该运算有如下特性:
- 一个数和0做异或运算,等于它本身
- 一个数和自身做异或元素,等于0
- 且该运算满足交换率和结合律
例如:
nums = [2, 2, 1] => 2 ^ 2 ^ 1 => 0 ^ 1 = 1
nums = [2, 1, 2] => 2 ^ 1 ^ 2 => 2 ^ 2 ^ 1 = 1
所以只需要将数组中的每个数,进行 异或
操作即可得出答案,且空间复杂度为 O(1)
/**
* @param {number[]} nums
* @return {number}
*/
var singleNumber = function(nums) {
let ans = 0;
for(let num of nums) {
ans ^= num;
}
return ans
};
小结
位运算在平时的工作中,使用到的场景较少,但是在一些特殊场景中,它的运算效率非常高;如果没记错的话,React
的源码中,在计算任务的优先级 lane
的时候,就用上了位运算哦~
LeetCode 👉 HOT 100 👉 只出现一次的数字 - 简单题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄