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的倍数。求出以后,对所有为mod 3就行了。得到的是最后要找的数据。
class Solution {
public:
int singleNumber(int A[], int n) {
int bitnum[32]={0};
int res=0;
for(int i=0; i<32; i++){
for(int j=0; j<n; j++){
bitnum[i]+=(A[j]>>i)&1;
}
res|=(bitnum[i]%3)<<i;
}
return res;
}
};
这里是以位为主循环,一次求所有数据的某一位的个数。然后对res或运算。
时间:O(32*N),这是一个通用的解法,如果把出现3次改为 k 次,那么只需模k就行了。
方案二:
利用三个变量one,two,three。
one的位表示只出现了一次,two的位表示出现了两次,那么three = two & one就表示为1的位已经出现了三次。那么最后将one和two中对应的位清零。
当然函数进入的时候,先通过two |= one & a[i]计算所有出现两次的位。结合上面说的one中的位值出现了1次,和a[i]相与后,得到了当前已经出现了两次的位。当然由于two是或的,可能已经有包含的1,则该位就出现了四次。先往下走。
然后是one = one ^ a[i];由于已经把都为1的值保存在two中,这里也就清掉了为one和a[i]中都为1的位。但如果one的第k位为0,a[i]的第k位也为0,会将one置为1。这也无妨当出现a[i]为3个的时候,会将该k位清除的。
流程:
1、更新two |= one & a[i]
2、更新one ^= a[i]
3、更新three = one & two
4、清除one和two已经满足3个的位:one &= ~three;two &= ~three
class Solution {
public:
int singleNumber(int A[], int n) {
int one=0, two=0, three=0;
for(int i=0; i<n; i++){
two |= one&A[i];
one^=A[i];
//cout<<one<<endl;
three=one&two;
one&= ~three;
two&= ~three;
}
return one;
}
};