认识异或运算

1.什么是异或运算

异或运算是位运算的一种,符号为:^
运算规则为:相同为0,不同为1
例如
认识异或运算_第1张图片
性质:

N ^ 0 = N
N ^ N = 0
A ^ B = B ^ A
(A ^ B) ^ C = A ^ (B ^ C)
  • N ^ 0 = N
public class XorOperation {
    public static void main(String[] args) {
        System.out.println(zeroXorNonZeroNum(5)); // 5
    }

    // 0与非零数异或
    private static int zeroXorNonZeroNum(int n) {
        return 0 ^ n;
    }
}

位与位异或,相同为0,不相同为1,所以b的结果为5


  • N ^ N = 0
public class XorOperation {
    public static void main(String[] args) {
        System.out.println(nonZeroXorSelf(5)); // 0
    }
    
    // 非零数与自身异或
    private static int nonZeroXorSelf(int n) {
        return n ^ n;
    }
}

两个相同的数,二进制都是一样的,所以在位与位进行异或的时候都相同,每一位的结果都是0


  • 交换律
public class XorOperation {
    public static void main(String[] args) {
        System.out.println(twoNonZeroNumXor(3, 8)); // 11
        System.out.println(twoNonZeroNumXor(8, 3)); // 11
    }

    // 两个非零数异或运算
    private static int twoNonZeroNumXor(int a, int b) {
        return a ^ b;
    }
}

因为异或是位与位进行计算的,所以异或的顺序不重要,结果是一样的


  • 结合律
int a = 2;
int b = 5;
int c = 4;
int d = (a ^ b) ^ c;
int e = a ^ b ^ c;
int f = a ^ (b ^ c);
System.out.println(d == e);	//true
System.out.println(f == e);	//true

通过交换律得知,异或的顺序不会影响结果,所以在某一些数先异或,再异或另个数的时候,也不会影响结果,这就是异或满足结合律

2.异或运算的本质

异或运算的本质是不带进位的加法

2.1 2阶异或运算

来看一个例子:
(10进制)5 = (二进制)00000101 (10进制)6 = (二进制)00000110
5 ^ 6 计算过程如下:
认识异或运算_第2张图片
相同为0,不同为1

2.3 3阶异或运算

来看一个例子:
(10进制)5 = (三进制)00000012 (10进制)6 = (三进制)00000020
5 ^ 6 计算过程如下:
认识异或运算_第3张图片
2 ^ 0 = 2,1 ^ 2 = 0

2.4 4阶异或运算

来看一个例子:
(10进制)5 = (四进制)00000011 (10进制)6 = (四进制)00000012
5 ^ 6 计算过程如下:
认识异或运算_第4张图片
1 ^ 2 = 3,1 ^ 1 = 2

3.异或运算的应用

3.1 交换两个变量的值

代码如下:

public class XorOperation {
    public static void main(String[] args) {
        int a = 4;
        int b = 5;
        System.out.println("==== exchange before ====");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println("==== exchange after ====");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

运行结果

==== exchange before ====
a = 4
b = 5
==== exchange after ====
a = 5
b = 4

3.2 如何把一个数字x最右侧那个1拿出来,变成00…10…的格式

代码如下

public class XorOperation {
    private static int getRightOne4Num(int n) {
        return n & (~n + 1);
    }
    
	public static void main(String[] args) {
        System.out.println(getRightOne4Num(5));
    }
}

运行结果

1

举一个例子:
比如取出5的最右侧为1的二进制数字
认识异或运算_第5张图片

3.3 统计一个二进制数中1出现的次数

代码如下

public class XorOperation {
	public static int countBinaryOne(int n) {
        int count = 0;
        while (n != 0) {
            int rightOne = n & (~n + 1);
            count++;
            n ^= rightOne;
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(countBinaryOne(5));
    }
}

运行结果

2

3.4 在一个数组中,只有一种数,出现奇数次,如何把它找出来

代码如下

public class XorOperation {
// 数组中,只有一种数,出现奇数次
    public static void printOddTimesNum1(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        System.out.println(eor);
    }
    public static void main(String[] args) {
        printOddTimesNum1(new int[]{1, 2, 1, 1, 4, 5, 4, 5, 2});
    }
}

运行结果

1

3.5 在一个数组中,有两种数,出现奇数,如何把这两种数找出来

代码如下:

public class XorOperation {
	public static void printOddTimesNum2(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        int rightOne = eor & (~eor + 1);// 取出最右边的1
        int onlyOne = 0;
        for (int i = 0; i < arr.length; i++) {
            if ((arr[i] & rightOne) != 0) {
                onlyOne ^= arr[i];
            }
        }
        System.out.println(onlyOne + " " + (eor ^ onlyOne));
    }
	public static void main(String[] args) {
        printOddTimesNum2(new int[]{1, 2, 1, 1, 4, 5, 4, 5});
    }

运行结果:

1 2

4.总结

  • 异或运算的本质是k阶不带进位的加法运算
  • 偶数个k异或运算的结果为0
  • 奇数个k异或运算的结果为k本身
  • x异或0=x,x异或x=0,异或运算不考虑顺序

你可能感兴趣的:(Java基础,java)