力扣面试题56-1:数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
要求时间复杂度为O(n),空间复杂度为O(1)。
如果使用哈希表来进行访问存储,空间复杂度不符合要求。
如果对每个数进行搜索,时间复杂度也不符合要求。
下面就介绍一种面对搜索不重复数字的一种特殊方法:异或。
数组中只有一个不重复数字的解法
如果只有一个重复数字,可以直接对整个数组中的每个数字进行异或。如果两个相同的数异或那么就会得0。因此,如果数组中所有的数都是成双出现,只有一个数字是单独出现的,那么异或之后得出的数就是那个单独出现的数字。
数组中有两个不重复数组的解法
而本题中有两个不重复数字出现,那么就不能简单的对所有数字都进行异或操作了。在这里介绍一种分组异或的方法,这种分组要求将两个单独出现的数字分到不同的组,而相同的数字分到一个组,如何实现这样的分组呢?
将数组中所有数都异或后得到的值为两个单独出现的数异或的值
在这种情况下,可以找到这个值的二进制表示中为1的位。这个位为1说明这两个数在这个位并不相同,因此按照这个位分组可以顺利的将两个单独出现的数分到不同的组中。
而对于那先相同的数,相同的数在同一个二进制位上的值肯定是相同的,因此按照这个二进制的值分组可以分出符合上述条件的组。
下面看代码:
class Solution {
public int[] singleNumbers(int[] nums) {
int k = 0;
for(int num : nums) {
k ^= num;
}
int mask = 1;
//找到为1的那一位(表示a与b在这一位不一样)
while((k&mask) == 0) {
mask <<= 1;
}
int a = 0, b = 0;
for(int num : nums) {
if((num&mask) == 0) {
a ^= num;
}
else {
b ^= num;
}
}
return new int[]{a,b};
}
}