原题:
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?
注意:
=>你的算法需要一个线性的复杂度,能否不适用额外的空间来实现这个功能。
class Solution {
public:
int singleNumber(int A[], int n) {
}
};
晓东分析:
其实在之前的博客中,我们曾今分析过其它元素都出现了两次,然后找出只出现一次的方法,见《Single Number--找出数组中唯一的一个只出现一次的元素》,当时使用的是一直异或来解决的。看到这个的问题的时候,我们不禁会想,是否也有同样的方法来解决呢,什么情况下三个相同的数字才能消除呢,这样的想法很不幸,基本就走入了死胡同。所以有时我们需要换一个角度来思考问题,我们仍然从位运算来考虑。一个数从二进制的角度来看,无非就是0和1,若是我们只从各个位来看,就把这一位的内容加起来,除以3,剩余的余数应该就是单独剩下的这个数在这一位上的值。有了单独这个数在各个位的值,这一个剩下的数也就出来了。这样来看,我们需要一个大小为32的数组来保存这个数,这个空间并不会随着数组n值的增加而变化,所以从空间角度来看是没有问题的。
代码实现:
class Solution {
public:
int singleNumber(int A[], int n) {
int bit_time[32] = {0};
int i = 0;
int j = 0;
int result = 0;
for(i = 0; i < 32; i++){
for(j = 0; j < n; j++){
bit_time[i] += (A[j] >> i) & 0x01;
}
result |= (bit_time[i] % 3) << i;
}
return result;
}
};
运行结果:
11 / 11 test cases passed.
| Status:
Accepted |
Runtime: 80 ms
晓东分析二:
看了上面的分析之后,我们突然会想是否有一些更简洁的方法来实现这个问题。同样从位运算的角度来看,我希望一个数用来表示出现一次的位,也就是说这个数的为1的位就表示这个位上面的数出现过了一次,比如0x10001,就表示bit[0]和bit[4]就是出现过了一次的位。然后再用一个数表示出现过了两次的位,再用一个数表示出现过了3次的位。只要这个位出现过了3次,我就把这个位拿清除掉,这样剩余的最后出现过一次的位的这个数就是我们要找的数了。
代码实现二:
class Solution {
public:
int singleNumber(int A[], int n) {
int ones = 0;
int twos = 0;
int threes = 0;
int i = 0;
for(i = 0; i < n; i++){
twos |= ones & A[i];
ones ^= A[i];
threes = ones & twos;
ones &= ~threes;
twos &= ~threes;
}
return ones;
}
};
运行结果二:
11 / 11 test cases passed.
| Status:
Accepted |
Runtime: 24 ms
|
从两次的运行结果来看,显然第二次的运行速度24ms要优于第一次的80ms。
希望大家有更好的算法能够提出来,不甚感谢。