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?
算法一,
每个数都出现3次,只有一个数出现1次。
以二进制的眼光来看,统计数的每个bit位为1的个数。如果每个数都出现3次的话,则每位1出现的总次数一定为3的整倍数。
设立32个计数器,统计整数每个位上1出现的总次数。再对3取余。结果则为只出现1次的整数的对应bit位的值。
在leetcode上实际执行时间为20ms。
class Solution { public: int singleNumber(vector<int>& nums) { const int BITS = sizeof(int) * 8; vector<int> counts(BITS); for (auto num: nums) { for (int i=0; i<BITS; i++) if (num & 1 << i) counts[i]++; } int ans = 0; for (int i=0; i<BITS; i++) { if (counts[i] % 3) ans |= 1 << i; } return ans; } };
不过由于题目要求不使用额外的辅助空间,且提示用位运算,故此代码不是特别满足题目要求。但提供了一种好的思路。
算法二,位运算
思路同上,但是采用位运算。
算法一是统计完1出现的总次数,然后对3取余。
可以改进为,在累加过程,将累加和进行即时对3作取余操作。即到3后,就自动溢出。
那么只需要3种状态, 即0,1,2。
如果用二进制来表示3状态的话,只需要2个bit位。则三状态为:00,01,10。
一个整数提供32个计数器,每个计数器为1位。
则采用2个整数,提供32个计数器,每个为2位。 其中一个计数器的权重为1(下面代码的变量one),另一个计数器的权重为2(下面代码变量two).
剩下要解决的是,如何作累加统计;以及如何溢出。
累加 可以用 异或, 还有逻辑与,两种运算代替。
简单的说,异或运算,就是不带进位的加法。 而 进位,则用 逻辑与 来处理。
对应下面代码来讲,两数加相加结果存入one中,如果有进位,将对应位存入变量two中。
然后要解决的问题,就是溢出问题(或者说对3取余)。 即到3了。自动回到0.
变量threee,就求出了,那些值已经为11的计数器。three的如果一个位为1,则表示对应的计数器已经为3. 则需要置该计数器为0.
此代码在leetcode上实际执行时间为12ms。
class Solution { public: int singleNumber(vector<int>& nums) { int two = 0, one = 0; for (auto num: nums) { two ^= one & num; one ^= num; const int three = two & one; two &= ~three; one &= ~three; } return one; } };
算法三
上面位运算还有种更简化的写法。不过比上面的要难懂一些。
但是基本思路一样。都是围绕 00, 01, 10, 这三种状态转换作文章。即每种状态在加1时,如何转到下一个状态。
1. 当高位为0时,低位可以作加法运算(异或)。 此时,可从 00, 转换成 01; 01 转换成 00.
对于后者, 由于产生了进位, 还需要处理进位。此时高位需再进行一次加法运算。
2. 当高位为1时,低位必为0. 此时,对着高位加进法。 完成从 10 转变成 00.
public int singleNumber(int[] A) { int ones = 0, twos = 0; for(int i = 0; i < A.length; i++){ ones = (ones ^ A[i]) & ~twos; twos = (twos ^ A[i]) & ~ones; } return ones; }此写法来自:
https://leetcode.com/discuss/6632/challenge-me-thx
原文地址:http://blog.csdn.net/elton_xiao/article/details/46286661