137 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
输入: [2,2,3,2]
输出: 3
链接:https://leetcode-cn.com/problems/single-number-ii
位运算复习
~ | 非运算 | ~0 = 1,~1 = 0 | 例:~10101 = 01010 |
---|---|---|---|
& | 与运算 | 1&1 = 1 ,1&0 = 0,0&0 = 0 | 例:10010&00110 = 00010 |
| | 或运算 | 1|1 = 1,1|0 = 1,0|0 = 0 | 例:1100101|1001100 = 1101101 |
^ | 异或运算 | 1^1 = 0,1^0 = 1,0^0 = 0 | 例:10010^01110 = 11100 |
<< | 左移 | 向左移动N位,0填补 | 例:10010<<3 = 10010000 |
>> | 右移 | 向右移动N位,0填补 | 例:10010>>3 = 00010 |
位运算解法
解法思路来自:https://leetcode-cn.com/problems/single-number-ii/solution/single-number-ii-mo-ni-san-jin-zhi-fa-by-jin407891/
感觉比官方的位运算好理解,豁然开朗。
一个概念:异或 实际上是做不进位的加法
1^0 = 1 + 0 = 1
1^1 = 看作 1+1 但我们不进位 = 0
即如果是在一组出现偶数次的数中寻找只出现一次的数,那就是所有数做异或运算的结果。
所以,我们的题目是在出现三次的数组中找出现一次的数,那我们就相当于实现一个三进制的异或运算。
这样,我们的关键就在于如何实现一个三进制的异或
我们需要构建三个变量来记录出现的次数:one、two、three 来对数组nums 进行遍历。
one : 二进制某位出现2次时ones = 0,出现1, 3次时ones = 1
two : 二进制某位出现1次时twos = 0,出现2, 3次时twos = 1
three:二进制某位出现3次时(即twos = ones = 1时)three = 1,其余即出现1, 2次时three = 0;
然后我们对one,two实现一个三进制的一个异或,即将出现三次的位归零。
这样 one 的最终结果就是出现一次的那个数
具体代码如下:
public int SingleNumber(int[] nums) {
int one = 0;
int two = 0;
int three = 0;
foreach (var n in nums)
{
two |= one&n;
one ^= n;
three = two & one;
one &= ~three; // 将二进制下出现3次的位置零
two &= ~three;
}
return one;
}
时间复杂度:O(n) 空间复杂度:O(1)