Single Number
题目描述
给出一个数组,数组中只有一个数字只出现了一次,其余数字都出现了两次,求这个只出现了一次的数字
解题思路
这里我们用到位运算异或^的特性a ^ a = 0
和0 ^ a = a
不难看出仅需要O(n)的时间内就可以解出答案,代码如下:
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for(int i=0;i<nums.length;i++)
ans = ans ^ nums[i];
return ans;
}
}
Single Number II
题目描述
给出一个数组,但是每个数都出现了3次,仅一个数出现1次,求出现1次的这个数
参考链接
- leetcode 之 Single Number II
- Detailed explanation and generalization of the bitwise operation method for single numbers - LeetCode Discuss
解题思路
这里我们仍然用位运算^ 但是与上一个题略有不同
在介绍位运算的具体做法之前,我们首先给出一种更加朴素的想法:统计每一位1出现的次数并取余。在不考虑答案时,其余数字都出现了三次,因此对每一位的统计结果也必然是3的倍数,显然对于统计结果求余就是出现一次的数字,也就是答案
s t a t i c s [ i ] = 3 ⋅ n + ( ( a n s ≫ i ) & 1 ) statics[i] = 3\cdot n + ((ans \gg i) \& 1) statics[i]=3⋅n+((ans≫i)&1)
但是用一个32位int来记录一个不大于4的数有点大才小用,实际上2bit足以,所以我们把两个int竖起来用
这样就极大的节省了空间,但是怎么实现求余的操作呢?这时我们不妨列出真值表
high | low | input | high_out | low_out |
---|---|---|---|---|
0 | 0 | 1 | 0 | 1 |
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 0 |
1 | 0 | 0 | 1 | 0 |
可知
l
o
w
_
o
u
t
=
h
i
g
h
ˉ
⋅
l
o
w
ˉ
⋅
i
n
p
u
t
+
h
i
g
h
ˉ
⋅
l
o
w
⋅
i
n
p
u
t
ˉ
h
i
g
h
_
o
u
t
=
h
i
g
h
ˉ
⋅
l
o
w
⋅
i
n
p
u
t
+
h
i
g
h
⋅
l
o
w
ˉ
⋅
i
n
p
u
t
ˉ
low\_out = \bar{high}\cdot \bar{low} \cdot input + \bar{high}\cdot low \cdot \bar{input} \\ high\_out = \bar{high}\cdot low \cdot input + high \cdot \bar{low}\cdot \bar{input}
low_out=highˉ⋅lowˉ⋅input+highˉ⋅low⋅inputˉhigh_out=highˉ⋅low⋅input+high⋅lowˉ⋅inputˉ
将
h
i
g
h
_
o
u
t
high\_out
high_out转为依赖
l
o
w
_
o
u
t
low\_out
low_out则有
high | low_out | input | high_out |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
1 | 0 | 0 | 1 |
得
h
i
g
h
_
o
u
t
=
h
i
g
h
ˉ
⋅
l
o
w
_
o
u
t
ˉ
⋅
i
n
p
u
t
+
h
i
g
h
⋅
l
o
w
_
o
u
t
ˉ
⋅
i
n
p
u
t
ˉ
high\_out = \bar{high}\cdot \bar{low\_out}\cdot input + high\cdot\bar{low\_out}\cdot\bar{input}
high_out=highˉ⋅low_outˉ⋅input+high⋅low_outˉ⋅inputˉ
根据上述逻辑表达式,我们就可以完成
00
→
01
→
10
→
00
00\to01\to10\to00
00→01→10→00的逻辑循环,也就是取余操作。因为重复数字贡献的次数取余后为0,仅剩余答案的位信息,所以返回低位计数器x1就是所求答案
class Solution {
public int singleNumber(int[] nums) {
int x1 = 0,x2 = 0;
for(int i=0;i<nums.length;i++){
x1 = ~x2 & (x1 ^ nums[i]);
x2 = ~x1 & (x2 ^ nums[i]);
}
return x1;
}
}
Single Number III
题目描述
给一个数组,所以元素都出现了两次,有两个元素出现了一次,求这两个元素
解题思路
这个题是最为接近Single Number的。同样采用位操作^,对数组异或,得到两个出现一次元素x y的异或值sum = x ^ y
。根据异或的运算规则我们知道,某位为1,则表示x和y在该位处值不相同,知道了这个就可以把数组分成两组:一组在该位为0;另一组为1。由于其他元素都出现了两次,所以在加入分组是也是成对的加入(因为重复出现的元素是相同的),这样就转换为了两个Single Number问题的求解
class Solution {
public int[] singleNumber(int[] nums) {
int sum = 0;
int len = nums.length;
for(int i=0;i<len;i++){
sum = sum ^ nums[i];
}
int dif = sum & -sum;
int[] ans = new int[2];
for(int ele : nums){
if( (ele & dif) == 0)
ans[0] ^= ele;
else
ans[1] ^= ele;
}
return ans;
}
}