一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
限制:
2 <= nums.length <= 10000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:本题要达到题目所要求的复杂度是很有难度的。知识点是使用位运算。
我们首先要知道如下几个知识点:
0 ^ x = x
x & (-x)
为取出x
的最后一位的1
x ^ x = 0
根据剑指offer上的思路,我们可以先从只找一个出现一次的数开始入手,因为其他数字都是出现两次,那么我们可以直接遍历,可以写出类似于如下的代码:
for(int num : nums)
s ^= num;
return s;
因为如果两个数相同的话,异或得0
,所以我们肯定可以把最后一个只出现一次的数筛选出来。
那么如何筛选出,两个只出现一次的数呢?
我们依然可以先像上面那样,那么最后的结果为两个不同的数异或的结果。
例如输入:[1,2,1,3,2,5]
,那么结果s
为:s = 3 ^ 5
;即:0011 ^ 0101 = 0110 = 6
。
这个结果说明什么呢,根据异或的性质,说明3
和5
从左往右数起的第二位和第三位是不相同的,我们根据任意一位来进行分组,首先我们根据“x & (-x)
为取出x
的最后一位的1
”,这个性质来筛选出最右边的1
,即11111010(-6的补码)&00000110 = 00000010
,然后根据 这个来进行异或分组,因为3
和5
在这一位是不同的,那么必然可以分到不同的组,相同出现两个的数因为是相等的原因必然可以分到相同的组,这样我们就可以将3
和5
转化成“只找一个出现一次的数”这个问题了。
代码实现如下:
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int temp = nums[0];
for(int i = 1;i < nums.size();++i)
{
temp ^= nums[i];
}
int last_one = temp & (-temp);//找出最右边1的位置
vector<int> res(2,0);
for(int num : nums)
{
if(num & last_one) //分组1
res[0] ^= num;
else //分组2
res[1] ^= num;
}
return res;
}
};