菜鸟刷题之路——Q16

问题描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

解题思路

问题用HashMap解决是很容易想到的。使用异或运算的特点也可以解决该问题。

HashMap

  1. 将数组的值作为key存入HashMap:
    ——1)当该值存在于HashMap中,删除它
    ——2)当该值不存在加入它
  2. 最后整个HashMap中剩下的就是结果的两个值。遍历一次数组即可。由于只有两个重复的元素,所以HashMap的存储、查询效率都为O(1),这个比利用ArrayList下标来标记的效率高很多,因为当数组中某个数字很大时,ArrayList的空间就会极大浪费。Hash的方法是完全优于ArrayList下标标记的方法的。

位运算

  • 这里有个很特殊的条件,所有数字都出现了两次,这意味着我们把数组中所有数组依次进行异或运算,结果值一定是两个出现一次的数字异或的结果,因为两个相同的数异或的结果为0。
  • 但是我们并不知道两个数字中哪些位置上为1,哪些位置为0
  • res &= (-res) 计算这个可以将res的最低为1的数字保留,其他高位清零。这是利用补码的特点,读者可以自己取一个数试一下。
  • 比如1010的补码是0110,二者与的结果是0010。拿到这个结果我们将数组中的数字分成两类,一类是和res与为1,一类是和res与为0。我们的结果一定分布在两个类中(一类一个)。
  • 因为其他数重复两次, 所以我们继续对这两类数进行异或操作,那么一个类中的重复数字(重复的数字一定会分在一个类中)就会相互抵消,异或最后的结果就是我们要的值。

Code

// HashMap优化 时间复杂度为O(n) 空间复杂度为O(n)
  public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap<Integer,Integer> hm = new HashMap();
        for (int i = 0; i < array.length; i++) {
            if (!hm.containsKey(array[i])) hm.put(array[i],1);
            else hm.remove(array[i]);
        }
        Set<Integer> st = hm.keySet();
        Iterator<Integer> it = st.iterator();
        num1[0] = it.next();
        num2[0] = it.next();
    }
//位运算解法 时间复杂度为O(2n) 空间复杂度为O(1)
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int res = 0;
        for (int i = 0; i < array.length; i++) res ^= array[i];
        res &= (-res);
        for (int i = 0; i < array.length; i++) {
            if ((array[i] & res) == res) num1[0] ^= array[i];
            else num2[0] ^= array[i];
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值