【牛客 - 剑指offer】详解 JZ56 数组中只出现一次的两个数字 Java实现(HashMap方案 + 利用异或运算的形式)


剑指offer题解汇总 Java实现

https://blog.csdn.net/guliguliguliguli/article/details/126089434

本题链接

知识分类篇 - 位运算 - JZ56 数组中只出现一次的两个数字

题目

在这里插入图片描述

思路 & 代码

方案一 HashMap

实现步骤:

  1. 使用HashMap记录数组中每个数字出现的次数
  2. 创建一个大小为2的int数组
  3. 遍历HashMap,把出现次数为1的数字放入数组中
  4. 比较数组中两个数字的大小,将两个数字按照非降序的顺序排列
  5. 最后,将排序好的数组作为返回值即可
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param array int整型一维数组
     * @return int整型一维数组
     */
    public int[] FindNumsAppearOnce(int[] array) {
        // write code here
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int value : array) {
            map.put(value, map.getOrDefault(value, 0) + 1);
        }

        int[] res = new int[2];
        int i = 0;

        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                res[i++] = entry.getKey();
            }
        }

        if (res[0] > res[1]) {
            int temp = res[0];
            res[0] = res[1];
            res[1] = temp;
        }

        return res;

    }
}

方案二 异或运算

  • 异或运算满足交换率
  • 相同的数字作异或会被抵消掉,比如:a ^ b ^ c ^ b ^ c = a
  • 任何数字与0异或还是原数字

放到这个题目里面所有数字异或运算就会得到a ^ b,也即得到了两个只出现一次的数字的异或和

但是,我们想要的不是只出现一次的两个数的异或结果,而是具体得到那两个数,可以考虑将数组分成两部分,一部分为 a ^ b ^ c ^ b ^ c = a,另一部分为 b ^ x ^ y ^ y ^ x = b,怎样划分才能让a与b完全分开,且另外的数字也能成对的出现在一个组里呢?这是需要考虑的问题。

解决思路是:

  1. 计算数组中所有数字异或以后的结果temp
  2. 从右向左(其实,从右向左,从左向右都是可以的)遍历temp
  3. 找出二进制中第一位为1的位置,记录这个位置为1所表示的数字k。在 temp 中
    • 如果某一位等于1,那么说明,a 与 b 的二进制形式在这一位上是不相同的
    • 如果某一位等于0,那么说明,a 与 b 的二进制形式在这一位上是相同的

【举例】
2的二进制表示为010
4的二进制表示为100
2 ^ 4 = 010 ^ 100 = 110
从右向左看,第一位为1的是中间那位,说明,两个只出现一次的数字在第二位上的数字是不一样的,2的第二位是0,4的第二位是1

  1. 将数组中其他数字也按照第二位来进行分类,第二位都是1的为一组,进行异或运算,第二位都是0的为一组,进行异或运算,这样就能保证,相同的数字自然会被划分到另一边,且只出现一次的两个数字也会自然地分开

举例
在这里插入图片描述

import java.util.*;

public class Solution {
    public int[] FindNumsAppearOnce(int[] array) {
        int temp = 0;
        int res1 = 0;
        int res2 = 0;
        for (int i : array) {
            temp ^= i;
        }
        int k = 1;
        while ((k & temp) == 0) {
            k <<= 1;
        }
        for (int i : array) {
            if ((i & k) == 0) {
                res1 ^= i;
            } else {
                res2 ^= i;
            }
        }
        if (res1 > res2) {
            return new int[]{res2, res1};
        }
        return new int[]{res1, res2};
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值