异或(^)

  • 基本操作: 相同出0,不同出1
  • 另一个说法:无进位进行相加

比如
101011
^ 110101
= 011110
这里就是无进位的相加不需要管进位的问题
即直接相加不需要关系进位的问题

特点
1、 N ^ 0 = N (任何数异或上0都是自己) 可以用无进位相加来进行理解

2、N ^ N = 0 (因为二进制是一样的,相同出0)

3、异或符合 交换律结合律

a ^ b = b ^ a (a ^ b) ^ c = a ^ (b ^ c)



冒泡排序中使用异或的方式交换数据的方式

public static void swap(int[] arr, int i, int j) {
    // 使用异或的方式进行交换
    arr[i] = arr[i] ^ arr[j];
    arr[j] = arr[i] ^ arr[j];
    arr[i] = arr[i] ^ arr[j];
}

解析:

arr[i] = "A";

arr[j] = "B";

(1)  arr[i] = arr[i] ^ arr[j];     arr[i] = "A" ^ "B";

(2)  arr[j] = arr[i] ^ arr[j];     arr[j] = "A" ^ "B" ^ "B" ==>  "A" ^ 0  ==> "A"

(3) arr[i] = arr[i] ^ arr[j];      arr[i] = "A" + "B" + "A" ==> "0" + "B" ==> "B"

所有最后  :   arr[i] = "B";     arr[j] = "A";

注意

使用这个异或交换数据的时候,交换的数据一定不能在内存的同一片空间之中,不然数据会被清空

所以必须保证交换的数据必须在两个不相同的内存空间中



使用异或解决的二个算法题

有一堆数,里面有一个数的个数是奇数,其他的都是偶数

思路

采用异或的方式进行解决问题,保证时间复杂度为 O(n) 空间复杂度为 O(1)

public static void printOddNum1(int[] arr) {
    // 让这个数和所有的数进行异或
    int eor = 0;
    for (int num : arr) {
        eor ^= num;
    }
    // 最后出来就是那个奇数个的数
    System.out.println(eor);
}

原因

可以怎么写的原因:

1、因为无论异或的顺序是什么样子的,最后的结果都是一致的

2、当两个相同的数进行异或,结果就是0,当0和一个数进行异或,结果就是这个数

3、所有异或到最后只有的那个数就是那个奇数



有一堆数,里面有两个数的个数是奇数,其他的都是偶数(进阶)

思路

1、先进行异或处理,将所有的数进行异或,得到的值就是 两个奇数进行异或的值

2、因为这两个值是不相同的,所以肯定得到的值里面有一个位置为1

3、找到这个1的位置,再将这个位置为1的值进行异或,得到的值就是其中的一个

4、再将这个值和之前和所以值进行异或得到的值进行异或,得到的就是另一个值

public static void printOddNum2(int[] arr) {
    // 让这个数和所有的数进行异或
    // 假如 第一个奇数为 a 第二个奇数为 b
    int eor = 0;
    for (int num : arr) {
        eor ^= num;
    }
    // eor = a ^ b
    // 找到最右边的1的位置 异或不同才出1
    // 00000100
    int rightNum = eor & (~eor + 1);
    // 将这个位置为1的数进行异或
    int eorOpen = 0;
    for (int num : arr) {
        // == 0 说明了在rightNum这个位置为 0
        if ((num & rightNum) == 0) {
            eorOpen ^= num;
        }
    }
    // eorOpen 不是 a 就是 b
    // 所以另一个数就是   eorOpen ^ eor
    System.out.println(eorOpen + " " + (eorOpen ^ eor));
}