一、题目内容
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
leetcode_137. 只出现一次的数字 II
二、题解思路
必备知识:这道题用到了|
、&
、>>
、<<
的操作,先来解释他们的含义
|
:有1为1,没1位0,例如1101 | 1000 = 1101
&
:都为1才是1,例如1101 & 1000 = 1000
>>
右移,例如2 >> 1 = 0010 >> 1 = 0100 = 4
<<
左移,例如2 << 1 = 0010 << 1 = 0001 = 1
逻辑原理:我们将所有的数看成是二进制位的数,先不考虑单独出现的那个数,用[2,2,3,2]来举例吧2换成2进制就是0010每个位置都相加
那如果该对应二进制上的数为1,那他们出现的次数一定是3的倍数,此时再把单独出现的那个数字加进去考虑
我们发现一个规律,如果每个对应的二进制为都模上3,结果肯定都是1或者0,如果结果为1,那它肯定是单独出现的那个数字二进制位上的。如果该数出现了3次、6次或者9次,模上3结果都为0,表示该单独的那个数字在该二进制位上并没有出现过,也就是该位置上为0。
代码实现原理(和代码一起看):每个数字都有32个比特位,所以我们循环32次,计算每个位上1出现的次数,里面再嵌套一个循环,用来遍历每个数,统计该位置上出现的次数。遍历时定义一个计数器,用于记录出现的次数,cnt+=(e>>i)&1
先看等式右边,e>>i
表示数字e右移i次后的结果,再与上1,1可以如果该位置上是1,那就加入到计数器cnt中。遍历完后,再看res|=(cnt%3)<<i
看等式右边cnt%3
表示看该位置上的1是否是单独出现的那个数字上的1,再用该结果左移i位,退回到原来二进制的位置,再看右边res|=
用结果或上等式右边的结果。
三、代码
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int res = 0;
for (int i = 0; i < 32; ++i)
{
int cnt = 0;
for (auto& e:nums)
{
//获取每个位上的个数
cnt += (e >> i) & 1;
}
//模3再右移i,表示该数原本应该在的位置,再或上之前的结果
res |= (cnt % 3) << i;
}
return res;
}
};