问题描述
Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
思路分析
一个数组中只有一个数只出现了一次,其他的数都出现过三次,在线性时间内不用额外的内存求出这个数。
之前出现两次的情况用异或就可以解决,因为一个数异或它本身是0,0异或一个数是这个数。而有三次的情况,我们使用两个bits来表示数出现的次数,使之出现00->10->01->00的循环,剩下的数就是要求的了。
引入ones和twos,如果一个数只出现了一次,会首先记录在ones里,然后并相当于还是0的非twos(此时为oxffffffff,全部为一,才能保存),这样ones就记录了;而如果出现了两次,ones会因为异或相当于清零,twos因为异或变为nums[i];第三次出现正好将twos也清零了。而只出现一次的数字,就在ones里了。
代码
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ones = 0, twos = 0;
for (int i = 0; i < nums.size(); i++){
ones = (ones ^ nums[i]) & ~twos;
twos = (twos ^ nums[i]) & ~ones;
}
return ones;
}
};
时间复杂度:
O(n)
O
(
n
)
空间复杂度:
O(1)
O
(
1
)
反思
来自discussion区,如果一个数出现5次的情况,也是同样的循环处理:
Here is another example. If a number appears 5 times at most, we can write a program using the same method. Now we need 3 bits and the loop is 000->100->010->110->001. The code looks like this:
int singleNumber(int A[], int n) {
int na = 0, nb = 0, nc = 0;
for(int i = 0; i < n; i++){
nb = nb ^ (A[i] & na);
na = (na ^ A[i]) & ~nc;
nc = nc ^ (A[i] & ~na & ~nb);
}
return na & ~nb & ~nc;
}
使用位运算符的操作非常巧妙,对于整型效果尤为显著。