题目
LeetCode: 260. Single Number III
Given an array of numbers nums
, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
给定一个整数数组 nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
Example:
Input: [1,2,1,3,2,5]
Output: [3,5]
Note:
-
The order of the result is not important. So in the above example,
[5, 3]
is also correct.结果输出的顺序并不重要,对于上面的例子,
[5, 3]
也是正确答案。 -
Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
解析
大致思路:
-
利用XOR异或运算的运算性质
a^0 = a, a^b^b = a, a^b = b^a
,对原始数组中的所有数进行异或求和,由于原数组中有两个数出现1次(设为a,b
),而其他数出现两次,因此由异或运算性质可得,异或求和的结果(设为xorSum
)即等于a^b
。 -
而两个数异或运算的结果,即代表了这两个数2进制各个数位上的异同,若-
xorSum
某个bit位上的数值为1(设此bit位为从右往左第一个数值为1的bit),则代表a,b
对应bit位上的值不同,可以按照此bit位上的值是否为1,将原始数组中的数分为两类,故a,b
必定在不同的两个类别中,且每个类别中的数除了a
和b
以外,其他的数必定出现2次,于是又可以利用异或运算的性质,对两个类别的数直接分别进行求和,两个异或运算之和即直接为两个待查找数a
和b
。
PS:
计算某个数x
的右边第一个数值为1的bit位对应十进制数公式如下:
x & (-x)
,或x & ((~x)+1)
代码
class Solution {
public int[] singleNumber(int[] nums) {
// 排除特殊情况
if (nums == null || nums.length < 2) return null;
// 遍历数组,计算异或和
int xorSum = 0;
for (int num : nums) {
xorSum ^= num;
}
// 计算异或和值右边第一个数值为1的bit对应的十进制数
int rightFirstBitOne = xorSum & ((~xorSum) + 1);
// 按照rightFirstBitOne对应bit位的值,将原始数组进行分组
// 然后计算其中某一类的异或之和,即为某个待查找数
int a = 0, b = 0;
for (int num : nums) {
// 只需要求出其中一个数a即可
if ((num & rightFirstBitOne) == 0) {
a ^= num;
}
}
// xorSum = a^b,故 xorSum^b = a^b^a = b
b = xorSum ^ a;
// 返回找到的两个数
return new int[]{a, b};
}
}
推广
对于“数组中某2个数出现奇数次,其他数出现偶数次,查找这2个特殊数”的类似问题,都可以使用本题解法进行求和