001.异或运算

认识异或运算

异或运算:相同为0,不同为1

同或运算:相同为1,不同为0

能长时间记住的概率接近0%

所以,异或运算就记成无进位相加

异或运算的性质

1)0 ^ N == N N ^ N == 0

2)异或运算满足交换律和结合律

​ 相当于几个数在一起进行异或运算 不论数的顺序是否相同 得到的结果都是一样的

如下例子:

abc结果
1010
0011
1111
1001

上面的表格可以看出来,无论你的顺序是 a ^ b ^ c 还是 b ^ c ^ a 还是 b ^ a ^ c 等 得到的结果都是一样的,因为每一个位置上的结果与 a b c 对应位置的值有关,与其它位置无关。

上面的两个性质用无进位相加来理解就非常的容易

题目一 如何不用额外变量交换两个数

这种方式的前提是两个变量指向的内存不同才可以进行交换,否则计算处理等于0

int a = 甲 , int b = 乙;

a = a ^ b;

b = a ^ b;

a = a ^ b ;

最后的结果就是 a = 乙,b = 甲

证明:

int a = 甲 , int b = 乙;

a = a ^ b = 甲 ^ 乙

b = a ^ b = (a ^ b) ^ b = a ^ b ^ b = 甲 ^ 乙 ^ 乙 = 甲 ^ 0 = 甲

a = a ^ b = (a ^ b) ^ 甲 = 甲 ^ 乙 ^ 甲 = 乙

所以 a = 乙,b = 甲

public static void main(String[] args) {
    int a = 300;
    int b = 345;
    System.out.println("---交换之前---");
    System.out.println("a = " + a);
    System.out.println("b = " + b);

    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    System.out.println("---交换之后---");
    System.out.println("a = " + a);
    System.out.println("b = " + b);
	
    // 数组里面的值进行交换
    int[] arr = {3,1,100};

    System.out.println("---交换之前---");
    System.out.println("arr[0] = " + arr[0]);
    System.out.println("arr[2] = " + arr[2]);

    swap(arr, 0 , 2);
    System.out.println("---交换之后---");
    System.out.println("arr[0] = " + arr[0]);
    System.out.println("arr[2] = " + arr[2]);
}

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];
}
运行结果如下:
    ---交换之前---
    a = 300
    b = 345
    ---交换之后---
    a = 345
    b = 300
    ---交换之前---
    arr[0] = 3
    arr[2] = 100
    ---交换之后---
    arr[0] = 100
    arr[2] = 3

特别注意:

这种方式的前提是两个变量指向的内存不同才可以进行交换,否则计算处理等于0

例如:

public static void main(String[] args) {
  
    int[] arr = {3,1,100};
    System.out.println("---交换之前---");
    System.out.println("arr[0] = " + arr[0]);
    System.out.println("arr[2] = " + arr[2]);

    swap(arr, 2 , 2);
    System.out.println("---交换之后---");
    System.out.println("arr[2] = " + arr[2]); 
    System.out.println("arr[2] = " + arr[2]);
}

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[0] = 3
    arr[2] = 100
    ---交换之后---
    arr[2] = 0
    arr[2] = 0

题目二 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数。

所有的数都异或运算起来,因为异或运算的结果与数的位置无关,并且其他数出现的次数都是偶数次,只有一种数是奇数次,所以所有的数异或运算之后就是该种数

// arr中,只有一种数,出现奇数次
public static void printOddTimesNum1(int[] arr){
    int eor = 0;
    for (int i = 0; i < arr.length; i++) {
        eor ^= arr[i];
    }
    System.out.println(eor);
}

题目三 怎么把一个int类型的数,提取出最右侧的1来

例子:

00001110101000

00000000001000

00001010001010

00000000000010

n & ((~n) + 1) n 与 (n取反后再加1)

例如

n = 0 0 0 0 1 1 1 0 1 0 1 0 0 0

~n = 1 1 1 1 0 0 0 1 0 1 0 1 1 1

~n + 1 = 1 1 1 1 0 0 0 1 0 1 1 0 0 0

n & (~n + 1) = 00000000001000

题目四 一个数组中有两种数出现了奇数次,其他数都是出现了偶数次,怎么找到并打印这两种数。

首先进行分析

假设两种出现次数为奇数次的数分别是 a 或 b

那么数组中的数全部进行异或运算之后的结果就是 eor = a ^ b

eor 的结果肯定是不为 0 的(如果为0 表示 a == b)

那么 eor 不为0 的位置 就是 a 和 b 数的不同的位置了。

这里假设 eor = 0101100100

如果为1就代表 a 和 b 数不相同的 位置了

选择其中的一个位置就可以进行区分

这里选择最后一个为1 的位置 0000000100

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZGqIraid-1692525468340)(imgs/image-20230820174246775.png)]

然后在进行异或运算之前先判断一下这个位置不等于0的数

如果不为0然后进行异或运算之后的结果就是b

最后进行 eor ^ b = a ^ b ^ b = a

代码如下:

// arr 中,有两种数,出现奇数次
public static void printOddTimesNum2(int[] arr){
    int eor= 0;
    for (int i = 0; i < arr.length; i++) {
        eor ^= arr[i];
    }
    // eor = a ^ b
    // eor != 0
    // eor 必然有一个位置上是1
    int rightOne = eor & (~eor + 1); // 提取出最右的 1
    int onlyOne = 0; //eor
    for (int i = 0; i < arr.length ; i++) {
        if ((rightOne & arr[i]) != 0) {
            onlyOne ^= arr[i];
        }
    }
    System.out.println(onlyOne + " " + (onlyOne ^ eor));
}

题目五 计算出一个int类型的数中的二进制数为1的个数

该题目就是先找到最右边不为0的值

然后将count++

紧接着就进行异或运算将该位置上的1去掉,然后进行下一次的循环,直到将N赋值为0后就跳出循环,最后得到的结果就是 1 的个数。

public static int bit1Counts(int N){
    int count = 0;

    while (N != 0) {
        int rightOne = N ^ (~N + 1);
        count++;
        N ^= rightOne;
    }
    return count;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值