LeetCode琅琊榜第二十一层-只出现一次的数字(二进制算法)
文章目录
题目
题目浏览
关键信息提取
除了某一个元素仅出现过一次之外,其他都出现三次
2e-31 <= nums[i] <= 2e31 - 1
算法的时间复杂度应该是线性
题目信息解读
- 在数组中,只可能出现两个情况
- 某一个元素出想过三次
- 某一个元素出想过一次
- 每一个元素的取值范围都在一个
int
范围内
算法概述
哈希计数法
- 哈希计数法主要有两种
- 通过
HashSet
集合来计数,一般该集合处理的是出想过多次的元素 - 通过
HashMap
集合来计数,一般该集合处理的是只出现过一次的元素 - 很明显,这里采用的是**
HashMap
**集合来计数
- 通过
二进制求解法
- 该方法非常的妙,属于我们 力扣二十层的一个前置方法
算法详解
算法思想
哈希计数法
- 我们先遍历一次
nums数组
,通过一个HashMap
来记录每一个元素出现的个数 - 再遍历一次
HashMap
,找到只出现过一次的元素
二进制求解法
解析
- 该算法的突破口是我们的
二进制位
,因此我们先得到其每一个元素的二进制位
,并将其位置对正 - 针对于出现过三次的元素,他们的
二进制位
一定是一样的,即对应的二进制位上会出现以下两种情况1 + 1 + 1 = 3 % 3 = 0
0 + 0 + 0 = 0 % 3 = 0
- 不论是哪一种情况都是3的倍数,同理推理到所有出现过三次的元素,只要该元素出现过三次对应的二进制位上
%3
一定会等于0 - 如果最后求出来的结果
(res = sum % 3) != 0
, 说明,这个res一定是出现一次的二进制位,将这些二进制位记录下来即可获取出现过一次的数字
代码实现与分析
哈希计数法
class Solution {
public int singleNumber(int[] nums) {
var map = new HashMap<Integer,Integer>();
var target = 0;
for (var num : nums) {
// 如果该元素不存在,就赋值为0+1,否则是原来的大小+1
map.put(num,map.getOrDefault(num,0) + 1);
}
for (var res : map.entrySet()) {
if (res.getValue() == 1) {
target = res.getKey();
break;
}
}
return target;
}
}
二进制求解法
class Solution {
public int singleNumber(int[] nums) {
// target 用于记录最终的结果
var target = 0;
// 每一个元素都在int范围内,所以一共有32个bit位,循环32次
for (int i = 0; i < 32; i++) {
// 记录每一个二进制位的和
var sum = 0;
for (var num : nums) {
// 获取每一个二进制位的和,详情请看解析1
sum += (num >> i) & 1;
}
// 详情请看解析2
if (sum % 3 != 0) {
target |= (1 << i);
}
}
// 返回结果即可
return target;
}
}
解析1
- 当我们想获取某一个二进制位的时候最简单的方法就是
移位操作符 >> 或 <<
,第一次移动i = 0
位,第二次移动i = 1
位,正好把 第一个和第二个二进制位取到,后面同理 - 在这个基础上,我们需要
对结果&1
,可以把前面可能有1的二进制位去掉,只留对应的那一位 - 叠加即可
解析2
- 出现过一次的元素的二进制情况也只能有两个,即
0和1
- 如果是
0
,结果sum % 3 == 0
成立,这个二进制位默认是0,不需要改变 - 如果是
1
,结果sum % 3 == 0
不成立,这个二进制位默认是0,需要改变成1,所以最终只要改变是1
的情况
- 如果是
- 将
1<<i
就可以得到对应的二进制位
算法结论
二进制求解法是优于哈希计数法的,不信你可以验证一下
二进制求解法不仅可以用于本题,还能运用于力扣二十层中!