【位运算】异或

基本概念

符号:^,用于二进制运算,值相同时为0,不同时为1。
例:1 ^ 1 = 0;0 ^ 0 = 0; 1 ^ 0 = 1; 0 ^ 1 = 1;
特性:
1、恒等律:任何数异或0都等于它本身,例:X ^ 0 = X
2、归零律:任何数异或自身都等于0 ,例:X ^ X = 0
3、交换律:A ^ B = B ^ A
4、结合律:A ^ (B ^ C) = (A ^ B) ^ C

练习

1、给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。(LeetCode136)

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
思路:因为给定数组中除了一个数字是单独出现之外,其他数字都是成对出现的,遍历数组进行异或,根据交换律和结合律可得:
2 ^ 4 ^ 4 ^ 3 ^ 2 = (2 ^ 2 )^ (4 ^ 4) ^ 3
再根据归零率可得:
(2 ^ 2 )^ (4 ^ 4) ^ 3 = 0 ^ 0 ^ 3 = 0 ^ 3
最后由恒等率可得:
0 ^ 3 = 3
Java代码实现如下:

package com.leecode._136;

public class Solution {
    public static void main(String[] args) {
        int res = singleNumber(new int[]{2, 2, 1});
        System.out.println(res);
    }

    public static int singleNumber(int[] nums) {
        int a = 0;//定义一个变量接收值

        /*
         * 让数组中的元素异或,第一次先用a接收数组中第一个元素(恒等率、即为a = nums[0])
         * 之后每一次循环都是当前元素与上一次的结果异或
         * 第一次循环:a = nums[0]; a = 2
         * 第二次循环:a = nums[0] ^ nums[1];
         * 第三次循环:a = nums[0] ^ nums[1] ^ nums[2];
         */
        for (int b : nums) {
            a = a ^ b;
        }
        return a;
    }
}


2、一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字?

示例 1:
输入: [2,2,1,1,3,4,3,5]
输出: 4 5
示例 2:
输入: [4,1,2,1,2,3,5,4]
输出: 3 5
思路:对数组进行遍历异或,因为数组里只有两个数字是单独出现的,所以异或后的结果即为所需求解的两数异或的值。即:
2 ^ 2 ^ 1 ^ 1 ^ 3 ^ 4 ^ 3 ^ 5 = 4 ^ 5
现在得到了4 ^ 5的值,需要反向求解出4和5,这里只需要求解出一个值,就可以通过异或得出另外一个值。(4 ^ 5 ^ 4 =5)
4的二进制:100,5的二进制:101,4 ^ 5 = 100 ^ 101 = 1(001)
通过观察可以发现,4和5的低位第一位不同,这个特征可以用来区分4和5这两个数。
如何来得到这个特征值呢?
一个数可以&他的负数来得到这个特征值。
例:
假如13是两个数的异或值,我们要得到低位的1,可以通过
13 & -13来得到。(13的二进制为00000000000000000000000000001101,-13的二进制为11111111111111111111111111110011)
得到这个特征值后再次遍历数组,匹配到特征值的都进行异或,这样就能找到两个数中的一个数,然后用异或得到另外一个数。
Java代码如下:

package com.leecode._136;

public class NumOnlyOnce {
    /**
     * 找出只出现一次的两个数
     */
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 2, 3, 1, 5, 4, 8, 4, 8};
        int XOR = findXOR(nums);//找到所求两个数的异或值
        int firstDiff = finFirstDiff(XOR);//找出特征值
        int oneOfXOR = findOneOfXOR(nums, firstDiff);//找到其中一个数
        int anotherOfXOR = XOR ^ oneOfXOR;//通过异或得到另一个数
        System.out.println(oneOfXOR);
        System.out.println(anotherOfXOR);
    }


    public static int findXOR(int nums[]) {
        int res = 0;
        for (int a : nums) {
            res = res ^ a;
        }
        return res;
    }

    public static int finFirstDiff(int res) {
        return res & -res;
    }

    public static int findOneOfXOR(int nums[], int firstDiff) {
        int res = 0;
        for (int num : nums) {
            if ((firstDiff & num) == firstDiff) {
                res = res ^ num;
            }
        }
        return res;
    }

}


本文参考:https://www.lijinma.com/blog/2014/05/29/amazing-xor/
如有错误,欢迎指出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值