方法一:哈希表

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

//找到两个数不相同的第一位

while((k & temp) == 0)

k <<= 1;

因为两个数字不相同,我们就以这一位是否为1来划分上述的两个数组,相同的数字自然会被划分到另一边,经过异或运算都抵消,而a与b也会刚好被分开。

1

2

3

4

5

//遍历数组,对每个数分类

if((k & array[i]) == 0)

res1 ^= array[i];

else

res2 ^= array[i];

具体做法:

  • 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};
        }
        
    }
}