1 题目描述
给你一个整数数组
nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。
2 解题思路
假设有一个样例nums = [1,2,1,3,2,5]。想要将nums中只出现一次的数字拧出来我们需要观察到数组内的数字有一个特性就是除开只出现一次的数字之外,其余的数字都是成双成对出现的,如果我们把成双成对的数字给去掉只留下出现一次的数字之后是不是就能离答案更近一些了呢?
基于这个思想之下,我们该如何去除掉出现两次的数字呢,这时候我们就需要用到一个操作符,异或操作符:“ ^ ”。
异或操作符“ ^ ”:相同为0,不同为一。则0^N=N。N^N=0。
所以我们可以把nums= [1,2,1,3,2,5]的内容统统异或起来得到1 ^ 2 ^ 1 ^ 3 ^ 2 ^ 5 = 3 ^ 5。
我们就能得到一个110的二进制序列,这时候我们相当于把其他成双成对的元素都去掉了,只剩下由3和5异或而成的二进制序列。这时候就应该思考一个问题,如何通过得到的二进制序列将3和5区分开呢?
这时候还是能够利用异或操作符的特性,相同为1,不同为0。
我们可以观察到110这个二进制序列从右往左看的第一个1就代表了3和5的不同之处。所以我们可以通过这个1来对数组中的所有数以二进制的从右往左数的第二位是否为1来进行分组。
所以1,1,3为一组。2,2,5为一组。
再分别将两组内的成员全部异或起来可以得到res1 = 1 ^ 1 ^ 3 = 3。res2 = 2 ^ 2 ^ 5 = 5。
这样我们就得到了两个只出现过一次的数字。
3 Leetcode AC代码:
int* singleNumber(int* nums, int numsSize, int* returnSize) {
int value=0;
//全部异或
for(int i=0;i<numsSize;i++)
{
value^=nums[i];
}
int pos=0;
//找出不同
for(int i=0;i<32;i++)
{
if(value>>i&1)
{
pos=i;
break;
}
}
//分组异或
int res1=0;
int res2=0;
for(int i=0;i<numsSize;i++)
{
if((nums[i]>>pos)&1)
{
res1^=nums[i];
}
else
{
res2^=nums[i];
}
}
int *single_dog=(int *)malloc(2*sizeof(int));
single_dog[0]=res1;
single_dog[1]=res2;
*returnSize=2;
return single_dog;
}