[2021校招必看之Java版《剑指offer》-40] 数组中只出现一次的数字

1、题目描述

  【JZ40】一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
  知识点:数组,位运算,哈希
  难度:☆

2、解题思路

2.1 哈希法

  使用一个哈希map来记录每一个数字重复的次数,然后把重复次数为1的输出即可。
  具体步骤:
  1、定义一个HashMap<Integer, Integer>
  2、遍历数组,记录每个数字的出现次数;
  3、定义一个Queue,把出现次数为1的数字入队;
  4、队列出队即最终结果。

2.2 位运算法

  异或操作是:两个位,不同则为1,相同则为0。
  具有以下规律:
  1、n^0 = 0;
  2、n^n = 0;
  3、n^n^m = n^(n^m);满足交换律。
  假设输入的数组array = {a,b,c,d,e,f,a,b,c,d},其中字母表示数字。把array每一个数字都异或:
  int xor = a^b^c^d^e^f^a^b^c^d,根据交换律,等同于:
  int xor = (a^a)^(b^b)^(c^c)^(d^d)^(e^f),等同于:
  int xor = e^f
  因此,数组所有数字进行异或的结果等同于不同的两个数字异或的结果。
  下一步就是根据这个xor找出ef分别是多少。
  我们来分析这个xor,展开成二进制表示,肯定有些位为1,有些位为0。二进制的右侧为低位,左侧为高位。假设第3个位为1,那么可以推断:ef的第3个二进制位肯定是一个为1,一个位0,不然异或结果不可能是1。
  我们根据这个“第3个二进制位是否为1”作为条件,把array的数字分成两部分。那么,ef肯定不会在一个部分里面,且相同数字肯定在一个部分里。
  两部分数组分别为:arr1 = {a,a,c,c,e}arr2 = {b,b,d,d,f}
  然后我们再分别把arr1arr2异或一下:
  int xot_arr1 = a^a^c^c^e 根据异或特性,得xot_arr1 = e
  int xot_arr2 = b^b^d^d^f 根据异或特性,得xot_arr2 = f
  即ef都找到了。

3、解题代码

3.1 哈希法

package pers.klb.jzoffer.medium;

import java.util.*;

/**
 * @program: JzOffer2021
 * @description: 数组中只出现一次的数字
 * @author: Meumax
 * @create: 2020-07-04 10:37
 **/
public class NumsAppearOnce {

    // num1,num2分别为长度为1的数组。传出参数
    // 将num1[0],num2[0]设置为返回结果
    public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        HashMap<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < array.length; i++) {
            if (map.containsKey(array[i])) {
                map.put(array[i], map.get(array[i]) + 1);
            } else {
                map.put(array[i], 1);
            }
        }

        Queue queue = new LinkedList();

        Set<Integer> set = map.keySet();

        for (Integer key : set) {
            if (map.get(key) == 1) {
                queue.add(key);
            }
        }

        num1[0] = (Integer) queue.poll();
        num2[0] = (Integer) queue.poll();
    }
}

  时间复杂度:O(N)
  空间复杂度:O(N)

3.2 位运算法

package pers.klb.jzoffer.medium;

/**
 * @program: JzOffer2021
 * @description: 数组中只出现一次的数字(位运算法)
 * @author: Meumax
 * @create: 2020-07-04 10:37
 **/
public class NumsAppearOnce {

    // num1,num2分别为长度为1的数组。传出参数
    // 将num1[0],num2[0]设置为返回结果
    public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        int xor = 0;

        for (int i : array) {
            xor ^= i;
        }

        int index = 1;  // xor从右到左第一个为1的位置

        while ((index & xor) == 0) {
            index = index << 1;
        }

        int result1 = 0;
        int result2 = 0;

        for (int j : array) {
            if ((index & j) == 0) {
                result1 ^= j;
            } else {
                result2 ^= j;
            }
        }

        num1[0] = result1;
        num2[0] = result2;
    }
}

  时间复杂度:O(N)
  空间复杂度:O(1)

4、解题心得

  类似这种找相同或找不同的数字,哈希法往往是最简单快捷的,笔试中优先考虑。在面试中,如果能够想出位运算的,说明基本功扎实,能给自己加分。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值