题目描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
示例1
输入:[1,4,1,6]
返回值:[4,6]
说明:返回的结果中较小的数排在前面
题目分析
计算数组中出现的数字次数的题,都可以使用hashmap来统计,最后遍历hashmap来获取满足要求次数的数字;
因为题目的其他数字都只出现两次,而相同的数异或结果为0,0与任何数相与仍然是原数,如果只有一个数不同的话,可以直接将所有的数异或即可;但是有两个数的话,所有数的异或结果相当于这两个数的异或结果,可以通过这个结果,来将数组分为两个部分,分别异或。
解题思路
方法1:使用hashmap统计数字出现的个数
1.使用hashmap,第一次出现的数,设置value为1,第二次出现,设置value为2;
2.通过map.entrySet()来遍历map中的数据,判断value是否为1,纪录结果;
方法2:使用异或运算,分开统计两个数字
1.首先将数组中所有数的进行异或,保存异或结果 x;
2.判断异或结果 x (二进制)第一次出现1的位置,对应的值为 mid,也即这两个数不同的地方;
3.通过判断和 mid相与是否为0,可以区分两个数字到不同的结果中,其他出现两次的数,一定会被分配到同一个部分;
4.对这两个部分分别进行异或,得到的两个结果即为目标数字。
代码实现
方法1:使用hashmap统计数字出现的个数
public int[] FindNumsAppearOnce (int[] array) {
int[] res = new int[2];
// 借助map来统计数字出现的次数
HashMap<Integer, Integer> map = new HashMap<>();
// 结果下标
int index = 0;
for(int i=0;i<array.length;i++){
if(map.containsKey(array[i])){
// 出现两次
map.put(array[i], 2);
}else{
// 首次出现
map.put(array[i], 1);
}
}
for(Map.Entry e:map.entrySet()){
// 遍历map中的数据
if(e.getValue().equals(1)){
// 找到出现1次的放到jied
res[index++] = (int)e.getKey();
}
}
// 让两个数有序
if(res[0] > res[1]){
int tmp = res[0];
res[0] = res[1];
res[1] = tmp;
}
return res;
}时间复杂度:O(n),需要遍历两次数组,花费时间为2*n,时间复杂度O(n);
空间复杂度:O(n),需要使用hashmap来保存出现的数字,花费的空间主要为 (n-2)/2 + 2,对于前面的算式,因为出现的数字除了两个数字是出现一次,其他的数字是出现了两次所以其他数字消耗的空间为 (n-2)/2 ,再加上这两个数字,总共消耗 (n/2 + 1),空间复杂度为O(n);
方法2:使用异或运算,分开统计两个数字
public int[] FindNumsAppearOnce (int[] array) {
// write code here
if(array == null || array.length <= 1) return new int[2];
// 获取两个数字的异或值,其他相同的异或为0
int x = 0;
for(int i=0;i<array.length;i++){
x ^= array[i];
}
int mid = 1;
// 找到异或值第一次为1的位置,即这两个数字第一次不同的位置
while((mid & x)==0){
mid = mid<<1;
}
// 设置两个结果值
int res1 = 0;
int res2 = 0;
for(int i=0;i<array.length;i++){
// 通过不同位置的值来区分这两个数
if((mid&array[i]) == 0){
res1 ^= array[i];
}else{
res2 ^= array[i];
}
}
// 让两个数有序
if(res1 > res2){
int tmp = res1;
res1 = res2;
res2 = tmp;
}
return new int[]{res1, res2};
}时间复杂度:O(n),需要遍历两次数组,和找到第一次出现1的位置(最多32次,属于常数),花费时间为2*n,时间复杂度O(n);
空间复杂度:O(1),需要使用常量个数的变量保存结果和中间结果,空间复杂度为O(1);

京公网安备 11010502036488号