面试题40. 数组中只出现一次的数字

题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
如:[2, 4, 3, 6, 3, 2, 5, 5],只出现一次的数字为4,6

思路:
本题的核心是使用异或运算异或运算的核心是:不同取1,相同取0
0 ^ 1 = 1 or 1 ^ 0 = 1
0 ^ 0 = 0
1 ^ 1 = 0
对于两个相同的数字,异或得到的结果为0,两个不同的数字异或得到的结果不为0
如:
4 ^ 4 = 0
4 ^ 6 = 2
了解了异或的原理后,再来看题目。在这个数组中,除了两个数字外,其他数字均出现了两次。我们对所有元素进行异或运算,即:2 ^ 2 ^ 3 ^ 3 ^ 5 ^ 5 ^ 4 ^ 6 。那些出现两次的数字进行异或计算后,得到的结果为0。本题中,2、3、5均出现两次,因此 2 ^ 2 ^ 3 ^ 3 ^ 5 ^ 5 = 0。最后只剩下数字4和6进行异或运算,4 ^ 6 = 2(0010),2的二进制中从右起第二位为1,说明4和6在第二位上不同。根据第二位上是否为1,可以将4和6分开到两个数组中,对于那些出现了两次的数字,会被同时分到某个数组中。因此,将数组分为两个部分:[2,2,3,3,6] 和 [4,5,5]。这种分法很巧妙,既可以将4和6分开到两个数组中,又可以将出现两次的数字分到同一个数组中。
对于这两个数组,对所有数字再一次进行异或运算,得到的结果就是只出现一次的数字。

综上,算法分为四个步骤:
Step1. 对数组中的每个元素异或运算,得到一个结果,记为num
Step2. 计算出 num的二进制中的,第一个1出现的位置(自右向左),记为position
Step3. 根据position位置上是否为1,将数组分为两个部分[2,2,3,3,6] 和 [4,5,5]
Step4. 分别对两个部分进行异或运算,得到两个结果就是只出现一次的数字

Java 代码如下:

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int[] array, int[] num1 , int[] num2) {
        if(array.length < 4) return ;

        // step1. 每个元素异或运算
        int num = findNumAppearOnce(array);

        // step2. 获取num的二进制中的第一个1出现的位置(自右向左)
        int position = getPositionOf1(num);

        // step3. 根据position位置上是否为1,将数组分为两个部分
        int[] arr1 = new int[array.length];
        int[] arr2 = new int[array.length];
        partitionBy1(array, position, arr1, arr2);

        // step4. 分别对两个部分进行异或运算
        num1[0] = findNumAppearOnce(arr1);
        num2[0] = findNumAppearOnce(arr2);
    }

    public int findNumAppearOnce(int[] array) {
        int num = array[0];
        for(int i = 1; i < array.length; i++) {
            num = num ^ array[i];
        }
        return num;
    }

    // 获取数字二进制中的第一个1出现的位置(自右向左)
    public int getPositionOf1(int n) {
        int position = 1;
        while(position <= n) {
            if((n & position) != 0) {
                break;
            }
            position = position << 1;
        }
        return position;
    }

    // 根据position位置上是否为1,将数组分为两个部分
    public void partitionBy1(int[] array, int position, 
                             int[] arr1, int[] arr2) {
        int p = 0, q = 0;
        for(int i = 0; i < array.length; i++) {
            if((array[i] & position) < position) {
                arr1[p++] = array[i];
            }else {
                arr2[q++] = array[i];
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值