异或运算练习:找出一组数中唯一(唯二)出现奇数次的数

题目描述

已知一个数组,现在请你用时间复杂度O(n),空间复杂度O(1)的算法求解:
1.假设数组中只有一个数的数量为奇数,其余都是偶数,请求出这个数。
2.假设数组中有两个数的数量为奇数,其余都是偶数,请求出这两个数。

问题一解法

问题一比较容易,只有一个数的数量为奇数,那么,设定一个变量,然后依次异或整个数组,最后剩下的数就是那个奇数。
原理分析:因为异或运算中,同一个数和自己本身异或的结果为0,0和任何数异或的结果不变,因此,所有偶数的异或结果为0,然后0和奇数个数的那个数异或的结果不变,因此,最后剩下的就是那个奇数。
代码

    public static int getOnlyJiShu(int[] arr){
        int res=0;
        for (int i = 0; i < arr.length; i++) {
            res=res^arr[i];
        }
        return res;
    }

问题二解法

1.首先,参考 问题一的思路,我们还是把数组中所有的数异或一遍,结果为那两个奇数异或。例如,数组中a和b的数量为奇数,那么,把整个数组异或一遍后,结果为a^b
2.拿到了a^b的结果,我们如何将ab分开呢?这里,可以想象,既然题目给了两个数,那么,ab就不可能相等,对吧?两个不相等的数,异或的结果,肯定至少有某一位二进制不为0。 为什么?因为异或运算就是相同为0,不同为1,如果ab异或的结果为0,说明ab每一位都相等,对吧?那a就等于b,显然,题目条件给的是ab不等,因此,ab异或的结果,肯定不为0,也就是说,肯定某一个二进制位不为0。
3.从第二步推理可以看出,a^b的结果不为0,那么,我们可以任意找一位1,1说明在这一位上,ab是不相同的。然后,再设定一个变量,根据这一位是0还是1,其实都可以,然后再次异或遍历整个数组,结果就是ab中的一个。
这一步怎么理解?可以理解为,根据ab异或为1的那一位,将ab在原来的数组中分到两个区间内,然后,在原来的数组中的一个区间内再次用异或运算遍历,就能得到ab中的一个数。至于两个区间分开后,其他偶数肯定也分开到两个区间了,并且相同的偶数肯定在同一个区间,因此,偶数异或的结果仍然全部是0。
4.最后,只需要用a^b的结果,再次异或ab中的一个,另一个也就求出来了。

代码

    public static void getTwoJiShu(int[] arr){
        int res=0;
        for (int i = 0; i < arr.length; i++) {
            res=res^arr[i];
        }
        //获取最右边的1
        int rightOne=res&(~res+1);
        int resOne=0;
        for (int i = 0; i < arr.length; i++) {
            if((arr[i]&rightOne)==0){
                resOne=resOne^arr[i];
            }
        }
        System.out.println(resOne+"   "+(resOne^res));

    }

代码中,如何获取最右边的第一位1的代码比较经典:
int rightOne=res&(~res+1);
~res是取反操作,二进制0变1,1变0
取反后加1,其实就是补码。
补码和原数相与的结果,就是原数右边第一位为1的数,其余都是0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值