一、需求
- 一个整型数组
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.1 思路分析
- 异或的特点:①相同为0,相异为1;②0与任何数异或就是原数;③任何一个数字与它本身异或结果为0;④常用1 & num 与 1左移的方式来判断num二进制位中第一位1(从低位到高位算);
- 给定的数组中有两个不同的数字,其余为相同的数字,根据异或特点③,所有数字异或的结果就是这两个不同数字的异或,我们无法从这两个不同数字的异或结果中找到这两个数字,因此需要分组;
- 那么如何分组呢?两个不同数字的异或结果中必然有1,我们根据异或特点④,找到为1的那一位,这两个数字在这一位,必然有一个为1,有一个为0;
- 假设第2位为1(从低位到高位算),则记mask = 0010(假设这里的二进制位数总共为4),使用mask与数组中的每个元素进行与运算,那么数组中的每个元素的二进制位在第2位(从低位到高位算)不是0就是1,其结果要么为0,要么为1,因为两个不同的数字在该位不同,故可分成两组;
- 对于数组中的相同元素,要么两个相同的数在第2位(从低位到高位算)均为0,要么两个相同的数在第2位(从低位到高位算)均为1,它们都在相同的组;
- 分组过程中对每组中的值进行异或操作,最后返回每组剩下的值就是要找的两个不同的数字;
2.2 代码实现
class Solution {
public int[] singleNumbers(int[] nums) {
int res = 0;
//res返回两个不同数字异或的结果
for(int i = 0; i < nums.length; i++) {
res ^= nums[i];
}
//寻找res的最低位1的位置
int mask = 1;
//'&'的优先级低于'=='
while((mask & res) == 0) {
mask <<= 1;
}
//开始分组
int z1 = 0;
int z2 = 0;
for(int i = 0; i < nums.length; i++) {
if((mask & nums[i]) == 0) {
z1 ^= nums[i];
} else {
z2 ^= nums[i];
}
}
return new int[] {z1, z2};
}
}
2.3 复杂度分析
- 时间复杂度为O(n);
- 空间复杂度为O(1);
三、学习地址
作者:eddieVim