一个整型数组 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/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第一种方法就是采用我们的map映射,先统计全部的元素的出现次数,然后将出现次数为1的数据封装到一个vector中并且返回这个容器
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
map<int ,int> record;
vector<int> result;
for(auto num:nums)
{
record[num]++;
}
for(auto num:nums)
{
if(record[num]==1)
{
result.push_back(num);
}
}
return result;
}
};
但是上面这种算法的效率非常低下,所以我们采用下面这种方法。
我们可以采用分组按位与的策略。也就是说:
我们首先将全部的元素都异或与到一起。(按位与的特性就是相同的元素异或到一起的话就会抵消)
所以这时候我们其实得到的是那两个落单的数字的异或的结果
然后我们从右往左,找到这个异或结果的第一个1,我们这里简称为x位。
为什么要找这个第一个1呢?
因为1的形成只可能是0和1进行异或。也就是说这两个数字中的其中一个这一位一定是1。(这两个数字不同导致异或的结果中一定有1存在)
然后我们将全部的数据分成两组,一组是当前这个x位的位置为1的组,另一组是当前这个x位为0的组。两组分别异或,就能分别从这两组中得到我们想要的结果。
假设数据的初始是
1
3
3
5
001
011
011
101
我们将全部的数据都异或(不相同为1,相同为0)到一起,得到100,
我们观察发现是从右往左第三位为1,所以我们将原来的数据分成两组,一组是二进制从右往左第三位是1的,另一组是从右往左第三位是0的
所以我们的1,3,3是一类,5是另外一类![]()
然后我们分别将每一组数据进行异或操作,1,3,3异或得到的结果是1(异或会将相同的元素抵消掉),另一组是5,这样我们就找到了这两个落单的数据1,5.
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int result=0;
//将我们全部的数据都异或到一起
for(auto num:nums)
{
result^=num;
}
//找到从右往左的第一个1的位置
int point=1;
while((point&result)==0)
{
point=point<<1;
}
//分组获取我们的两个落单的数据
int result_1=0;
int result_2=0;
for(auto num:nums)
{
//如果这个数据和我们上面取得的第一个1的位置按位与是0,就将其归为一类
if((point&num)==0)
{
result_1^=num;
}
//如果这个数据和我们上面取得的第一个1的位置按位与是point(1×2^n),就将其归为另一类
else
{
result_2^=num;
}
}
//两组数据异或之后,相同的元素会被抵消,不相同的元素会留下
//我们将我们上面获取到的结果封装到一个容器中,并将这个容器返回。
vector<int> final;
final.push_back(result_1);
final.push_back(result_2);
return final;
}
};