题解 | #数组中只出现一次的两个数字#
数组中只出现一次的两个数字
https://www.nowcoder.com/practice/389fc1c3d3be4479a154f63f495abff8
方法一:哈希表
1、利用哈希表进行统计数字出现的频率
2、再次遍历,找到只出现一次的数字,加入创建的ArrayList数组中
3、要求非降序输出,则先比较二者的大小后输出,注意要求返回的是int【】数组,要创建此数组将得到的值作为其元素
import java.util.*;
public class Solution {
public int[] FindNumsAppearOnce (int[] nums) {
HashMap<Integer,Integer> mp = new HashMap<Integer,Integer>();
//遍历数组,统计频率
for(int i=0;i<nums.length;++i){
if(!mp.containsKey(nums[i])){
mp.put(nums[i],1);
}else{
mp.put(nums[i],mp.get(nums[i])+1);
}
}
//再次遍历,寻找只出现一次的数
ArrayList<Integer> res = new ArrayList<Integer>();
for(int i=0;i<nums.length;++i){
if(mp.get(nums[i])==1){
res.add(nums[i]);
}
}
// 非降序输出,只有两个数,比较后返回
if(res.get(0)<res.get(1)){
return new int[] {res.get(0),res.get(1)};
}else{
return new int[] {res.get(1),res.get(0)};
}
}
}
方法二:位运算之异或运算
异或运算有个特点是能抵消相同的元素,即 a⊕b⊕c⊕b⊕c=a,任何数和0异或还是数字本身。正好题目中其他数字出现两次,两个特殊的数字出现一次,将所有数异或,结果就是两个特殊的数的异或值a⊕b,最后要分别取出两个特殊值。
0^0=0,0^1=1,1^0=1,1^1=0
根据a⊕b的结果中如果二进制第一位是1,则说明a与b的第一位二进制不相同,否则则是相同的,从结果二进制的最低位开始遍历,总能找到二进制位为1的情况:
0&0=0,0&1=0,1&0=0,1&1=1
1 2 3 |
|
因为两个数字不相同,我们就以这一位是否为1来划分上述的两个数组,相同的数字自然会被划分到另一边,经过异或运算都抵消,而a与b也会刚好被分开。
1 2 3 4 5 |
|
具体做法:
- step 1:遍历整个数组,将每个元素逐个异或运算,得到a⊕b。
- step 2:我们可以考虑位运算的性质,通过与运算从低位开始找到二进制中第一个不相同的位,即与运算结果为1的位(这是前面异或运算的结果,只有当1^0时结果才为1)
- step 3:遍历数组,根据前面判断的第一个不相同的位置是否为1,可以将所有数组划分成两组,对分开的数组单独作异或连算,结果就是两个不同的值。
- step 4:最后整理两个数的次序再输出。
代码如下:
import java.util.*;
public class Solution {
public int[] FindNumsAppearOnce (int[] nums) {
int res1=0;
int res2=0;
int temp=0;
//全部异或,得到两个数的异或值
for(int i=0;i<nums.length;++i){
temp^=nums[i];
}
int k=1; //记录从低位起第一个不相同的位
while((k&temp)==0){//结果为0,表示该位相同
k<<=1; //左移一位,继续比较
}
//再次遍历数组,将其按k的最高位划分为两组
for(int i=0;i<nums.length;++i){
if((k&nums[i])==0){ //该位为0是一组
res1^=nums[i]; //最后结果只有一个数
}else{ //该位为1是一组
res2^=nums[i];
}
}
if(res1<res2){
return new int[]{res1,res2};
}else{
return new int[]{res2,res1};
}
}
}
