问题1
给定一整形数组,已知该数组中一个数出现了一次,其余的数出现了偶数次,找到这个出现一次的数
解法一:使用hashMap记录次数,然后返回只出现一次的那个数
代码如下:
public int singleNumber(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for(int num : nums) {
int val = map.containsKey(num) ? map.get(num) + 1 : 1;
map.put(num, val);
}
for(int num : nums) {
if(map.get(num) == 1) {
return num;
}
}
return 0;
}
时间复杂度为O(N),额外空间复杂度O(N)
解法二:使用位运算。我们知道两个相同的数进行异或操作后的结果是0,任何数和0异或的结果是它本身,异或操作亦具有交换律。因此我们只需让数组中所有数异或到一起,结果即为所求。
代码如下:
public int singleNumber(int[] nums) {
int result = 0;
for(int num : nums){
result ^= num;
}
return result;
}
时间复杂度O(N),额外空间复杂度O(1)。
问题2
给定一整型数组,已知其存在两个数出现了一次,其它数出现了偶数次,找到那连个出现一次的数。
该问题依然可以使用HashMap进行求解,解法与问题一相同,不加赘述了。
该问题中由于含有两个出现一次的数,如直接全部异或的到的结果为这两数的异或,因此无法直接按问题一解法求解。
一个想法是将这两个数分开,分别求解,我们知道若两数相同则其在任一位都相同,若两数不同,则总是存在一位不同的位。由于我们已经得到了这两数的异或记做XOR1,找到XOR1中为1的那一位,可得该位上这两数一定不同,因此可以通过该位是否为1可以将数组分为两块,分别对这两块求异或即为所求。
实现代码如下:
public int[] singleNumber(int[] nums) {
int xor1 = 0;
for(int num : nums){
xor1 ^= num;
}
int number = 0; // 存储为1的位数
for(int i = 0; i < 32; i++){
if((xor1 & 1) == 1){
number = i;
break;
}
xor1 = xor1 >> 1;
}
int val1 = 0, val2 = 0;
for(int num : nums){
if(((num >> number) & 1) == 1){
val1 ^= num;
}else{
val2 ^= num;
}
}
return new int[] {val1, val2};
}