方法一:哈希表
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}; } } }