题目:
Given a non-empty 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?
例子:
Example 1:
Input: [2,2,3,2]
Output: 3
Example 2:
Input: [0,1,0,1,0,1,99]
Output: 99
问题解析:
给定一个非空整数数组,除了其中一个元素外,其余每个元素均出现三次。找出这个只出现一次的元素。
链接:
思路标签
位运算、状态机
解答:
- 和LeetCode 136:Single Number属于类似的问题,136中给出其他元素均出现两次,我们使用“异或”运算,即:
0^a=a
、a^a=0
,即可求得结果; - 但是当元素上升到3个及3个以上时,就无法继续使用这么简单的方法来实现了,下面给出三种实现方法,其中后面的两种是相同的,同时第一种和最后一种方法对于任意一个重复K次的相似问题均是通用的。
1. 统计每一位1的个数,和对3取余
- 对每个整数以32位表示,分别统计每一位上1的个数,最后该位数上的和对3取余数;
- 如果余数不为0,则说明该位上只出现一次的元素,在该位上有1;
- 通过移位操作和1做位与运算,则可求得当前元素在该位是否有1。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for(int i=0; i<32; ++i){
int sum = 0;
for(int j=0; j<nums.size(); ++j){
sum += (nums[j]>>i) & 1;
}
ret |= (sum % 3)<<i;
}
return ret;
}
};
2. 状态机法,用2个数分别表示状态机的3个状态
- 利用状态机表示:我们用下面的三种状态来表示对于某个数字出现了多少次:00、01、10、00,也就是我们累加的过程0——>1——>2——>0,当满3个数字时则记为0次。
- 所以,我们可以用两个整数(32位)来表示所有数中每一位上出现1的次数。例子:ones的第3位为1,twos的第3位为0,则表示当前累计的所有元素中,各个元素的第3位上出现1的个数为2个。
- 写出对应关系:
- 00 (+) 1 = 01
- 01 (+) 1 = 10
- 10 (+) 1 = 00 ( mod 3)
- 那么我们用ab来表示开始的状态,对于加1操作后,得到的新状态的ba(其中a表示倒数第一位,b表示倒数第2位)的算法如下:
- a = a xor r & ~b;
- b = b xor r & ~a;
- 因为我们最后要求的是只出现1次的元素,所以通过对所有元素的位运算后,32个位上出现1只有一次的元素就是我们要返回的元素,也就是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;
}
};
3. 状态机法,用K-1个数分别表示状态机的K个状态(通法)
- 下面的解法是与解法2同样思想的解法,但其可以解决任意K个重复元素的问题。
- 同时,对于不同的K,假设只有一个元素重复3次,其余重复K次,K>3,那么我们还可以用下面的方法返回
bits[2]
来得到该返回元素,具体就不展开分析。
class Solution {
public:
int singleNumber(vector<int>& nums) {
return singleNumber(nums, 3);
}
int singleNumber(vector<int>& nums, int _K) {
int K = _K - 1;
int bits[K] = {0};
for (int num : nums) {
for (int i = 0; i < K; i++) {
int mask = -1;
for (int j = 0; j < K; j++) if (j != i)
mask &= ~bits[j];
bits[i] = (bits[i] ^ num) & mask;
}
}
return bits[0];
}
};