题目:
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
1
2
Example 2:
Input: [0,1,0,1,0,1,99]
Output: 99
1
2
问题解析:
给定一个非空整数数组,除了其中一个元素外,其余每个元素均出现三次。找出这个只出现一次的元素。
链接:
LeetCode:https://leetcode.com/problems/single-number-ii/description/
思路标签
位运算、状态机
解答:
和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;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
};
1
2
3
4
5
6
7
8
9
10
11
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];
}
};
---------------------
作者:Koala_Tree
来源:CSDN
原文:https://blog.csdn.net/Koala_Tree/article/details/80228525?utm_source=copy