创建时间: November 25, 2021 3:15 PM
最后编辑时间: November 25, 2021 3:17 PM
标签: 位运算, 数组
网址: https://leetcode-cn.com/problems/WGki4K/
难度: 中等
题目
输入一个整数数组,数组中只有一个数字出现了一次,而其他数字都出现了3次。请找出那个只出现一次的数字。例如,如果输入的数组为[0,1,0,1,0,1,100],则只出现一次的数字是100。
分析
哈希表
本题一个直观的想法是使用哈希表来统计数字出现的次数,再遍历value,找到key
class Solution {
public int singleNumber(int[] nums) {
Map<Integer, Integer> freq = new HashMap<Integer, Integer>();
for (int num : nums) {
freq.put(num, freq.getOrDefault(num, 0) + 1);
}
int ans = 0;
for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
int num = entry.getKey(), occ = entry.getValue();
if (occ == 1) {
ans = num;
break;
}
}
return ans;
}
}
使用位运算
先来看一个简化的版本
输入数组中除一个数字只出现一次之外其他数字都出现两次,请找出只出现一次的数字
异或tip
任何一个数字异或它自己的结果都是0
任何一个数字异或0的结果都是它自己
a ^ a=0,a ^ 0=a,a ^ a ^ a=a
如果将数组中所有数字进行异或运算,那么最终的结果就是那个只出现一次的数字。
再看本题,因为其他数字出现了三次,一个数字异或自己两次得到的数字还是自己本身,因此不能采用异或法
一个整数是由32个0或1组成的。我们可以将数组中所有数字的同一位置的数位相加。如果将出现3次的数字单独拿出来,那么这些出现了3次的数字的任意第i个数位之和都能被3整除。因此,如果数组中所有数字的第i个数位相加之和能被3整除,那么只出现一次的数字的第i个数位一定是0;如果数组中所有数字的第i个数位相加之和被3除余1,那么只出现一次的数字的第i个数位一定是1。这样只出现一次的任意第i个数位可以由数组中所有数字的第i个数位之和推算出来。当我们知道一个整数任意一位是0还是1之后,就可以知道它的数值。
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for (int i = 0; i < 32; ++i) {
int total = 0;
for (int num: nums) {
total += ((num >> i) & 1);
}
if (total % 3 != 0) {
ans |= (1 << i);
}
}
return ans;
}
}
核心代码是total += ((num >> i) & 1);
和ans |= (1 << i);
(num >> i) & 1
是取num的第i个二进制位
ans |= (1 << i)
是将ans的第i个二进制位置1
数字电路设计优化
官方题解居然能想到用数字电路的方式来求解本题,也是我没想到的,过程没有看懂,但是代码是真的简洁,这里也贴一下
class Solution {
public int singleNumber(int[] nums) {
int a = 0, b = 0;
for (int num : nums) {
b = ~a & (b ^ num);
a = ~b & (a ^ num);
}
return b;
}
}