260. 只出现一次的数字 III
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0]
输出:[-1,0]
示例 3:
输入:nums = [0,1]
输出:[1,0]
提示:
2 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
除两个只出现一次的整数外,nums 中的其他数字都出现两次
题解:
方法一: 排序
- 我们可以直接排序,接着通过排序后规整的数组可以很容易的求解。
方法二:位运算
为了实现线性时间复杂度
与常数空间复杂度
,我们可以使用位运算
来解决。
首先理解题目,假设我们要得到的两个数分别为num1
和num2
,那么如果我们将数组内所有元素全部异或一遍,根据异或的性质以及题目的特殊性,我们可以知道最终得到的是num1和num2
异或后的结果。
这个时候我们可能会想,我们能否使用位运算的那些运算符来反推出num1和num2
究竟是谁?
- 但是通过下面一个例子我们会发现我们无法直接通过num1和num2异或后的结果来直接反推出num1和num2:
例:(我们设num1和num2异或得到的结果为s)
设num1为 :0000101
设num2为 :0000100
那么s为 :0000001
我们通过后三位来判断,从最后一位开始,我们发现1与0不同,所以按照异或的性质返回的是1;接着二者均为0,所以返回0;接着二者均是1,所以返回的也是0。
这个时候我们发现,如果s化为2进制的某一位为1
的话,我们还可以反推出num1和num2
此时对应的位应为一个1一个0
,但是如果s化为2进制的某一位为0
的话,我们发现num1
和num2
此时对应的位可以均为0
也可以均为1
,因此直接反推是不可行的!
既然根据s直接反推
是不可行的,那么我们还有没有别的方式来得到num1
和num2
呢?
- 既然直接反推不行,那我们可以凭借s去
筛选
,即由于我们无法直接反推出结果,可以根据s的一些特性
,来找到相互异或能合成这种特性
的两个数num1
和num2
! - 再回到上面分析反推不可行的步骤,我们发现当s某一位为1时,我们是可以确定得到
num1
和num2
此位应该一个为0
,一个为1
的,因此我们可以以此性质作为我们的筛选
条件去筛选! - 我们再次遍历
nums
数组,依据我们从s的二进制形式中选定的某一位(此位为1即可),依据nums
中元素在此位是呈现0还是呈现1来分组,因此我们可以将nums
中的数分为两组,接着让每组相互异或
,由于此数组只有两个数是只出现一次的,其他的数均是两两出现,且相同的数必定会分在一组中,因此他们两两异或为0,两两异或为0
,最后我们发现每组异或后的结果就是我们要的num1
和num2
。
代码:
class Solution {
public int[] singleNumber(int[] nums) {
// int[] temp = new int[2]; 排序法
// int index = 0;
// Arrays.sort(nums);
// int count = 1;
// int res = nums[0];
// for(int i=1;i<nums.length;i++){
// if(res==nums[i]){
// count++;
// }
// else{
// if(count==1){
// temp[index++] = res;
// count = 1;
// res = nums[i];
// }
// else{
// count = 1;
// res = nums[i];
// }
// }
// }
// if(count==1){
// temp[index++] = nums[nums.length-1];
// }
// return temp;
int[] res = new int[2];
int count = 0;
for(int i=0;i<nums.length;i++){
count^=nums[i];
}
int k = -1;
for(int i=0;i<32;i++){
if(((count>>i) & 1 )==1){
k = i;
break;
}
}
for(int num : nums){
if(((num >> k) & 1 )==1){
res[0] ^= num;
}
else{
res[1] ^= num;
}
}
return res;
}
}