位运算——异或

异或运算:对于一个数字的二进制形式的每一位,相同为0,不同则为1;

异或运算的简单性质(假如abc是两个不同的整数):a^b^c = a^c^ b(交换律)=a^(b^c)(结合律),a^a=0;a^0=a,a^b^a=b

以前我们写过很多次的整数交换:

int temp = a;
a = b;
b = temp;

但是如果我们不能使用额外的变量呢,那应该如何处理?这就得用到我们的异或运算了:

a = a ^ b;
b = a ^ b;
a = a ^ b;

啊???这就结束了吗?对,这就结束了。

接下来我们仔细看看这三行代码,到底是如何进行交换的:(方便观看,这里将运算过程写在上述代码片段的注释)

a = a ^ b;//此时a=a^b,b=b
b = a ^ b;//此时a=a^b,b=a^b=(a^b)^b=a^(b^b)=a^0=a
a = a ^ b;//此时a=a^b=(a^b)^b=(a^b)^a=b,b=a完成了交换

第二个经典的问题:一个整形数组中,只有一个数字只出现了奇数次,其余的整数均出现了偶数次,请找出这个出现奇数次的数:

我们可以根据异或运算的性质进行解决,我们知道,两个相同的整数异或得到的是0。所以最终出现偶数次的数全部运算成了0,而0^x=x,一次可以得到那个出现了奇数次的整数:

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

对于上面的问题升级一下,同样的数组,现在有两个数字出现了奇数次,其余数字均出现了偶数次,请找出这两个数字:

用样的,我们使用异或运算,最终得到的就是a^b的值,那么如何得到a和b的值?我们可以知道,对于相互异或的两个整数,他们的二进制为相异为1,相同为0,我们可以找到a^b的最右边的第一个为1的位,说明这两个数在这个为上一定不同。利用此特点,可以将数组分成两组进行计算。

public static int[] search2(int[] arr) {
        int[] temp = new int[2];
        int eor = 0;
        for(int i = 0;i < arr.length;i++) {
            //假设这两个数字就是 a  b
            eor ^= arr[i];//这里最终得到的结果就是a ^ b
        }
        //如果eor的某一位上为1,就说明a和b这两个数在这一位上一定不一样
        //据此可以将数组分成两部分,一部分是这一位为1的数,一部分是这一位为0的数
        //此时可以知道这两个数必然被分到不同的组中
        //eor继续与其中某一组数进行异或,就可以得到其中一个数
        //首先将eor最右侧的1提取出来
        int rightOne = eor & (~eor + 1);
        //这里定义一个变量来记录a ^ b的值
        int onlyOne = eor;
        for(int cur : arr) {
            if((cur & rightOne) != 0) {//只有当某个数在这一位为1才进行异或操作
                eor ^= cur;
            }
        }
        temp[0] = eor;//此时eor记录的就是其中一个数的值
        temp[1] = eor ^ onlyOne;

        return temp;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

假若爱有天意

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值