Given an array of integers, every element appears three times except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
思路:位运算神题!
这一题我们将所有的数都看作是二进制表示形式。
因为所有的数都出现了三次,除了我们要求的数出现了一次。
那么先考虑如果所有数都出现了三次是什么情况。
将所有数都看作二进制表示形式,然后对于所有的数,我们计数二进制每一位上1出现的次数。
因为所有数都出现了3次,因此每一位上的1也应该是3的倍数。
现在,再添加一个只出现了一次的数。
那么很容易发现,这个数所有二进制为1的位计数都加了1,于是这些位的计数就不再是3的倍数了!
整理下思路就是,这些二进制位1的计数,每到3就归零,则综合最后为1的位就是这个数了。
要计数二进制每一位1出现的次数,我们可以用数组,但是这里用两个int变量就够了。因为int本身就是一个32位变量,我们让其自身的每一位负责计数它对应那一位上1出现的次数。
因为我们对于1的计数到3后就归零,因此两位二进制就可以表示:00、01、10、00。。。。
然后我们将两个计数int变量命名为ones和twos,分别存储这个二进制计数的最低位和高位。
而他们的变化流程如下:
twos ones
0 0
0 1
1 0
0 0
先看ones的规律:ones与nums[i]异或,二进制位初始为0,与1异或1次后为1,异或2次后为0,异或3次后为1。但是根据上表,第三次时ones仍要为0,注意观察twos在第一次和第三次时的值,可以发现ones = (ones ^ nums[i]) & ~twos就能满足这个条件。
twos的规律类似, 为twos = (twos ^ nums[i]) & ~ones。这里,因为twos要在ones之后计算才能实现表中的效果。
最后所有值都异或完成后,所有出现3次的位都变为0,不是3次的则为1次。ones就是所求的数,而twos等于0。
1 class Solution { 2 public: 3 int singleNumber(vector<int>& nums) { 4 int ones = 0, twos = 0; 5 for (int num : nums) 6 { 7 ones = (ones ^ num) & ~twos; 8 twos = (twos ^ num) & ~ones; 9 } 10 return ones; 11 } 12 };