认=异=或=运=算


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

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

所以,异或运算就继承无进位相加!

异或运算的性质

1)0^N = N (用无进位相加来理解更容易记住)
2)N^N = 0(用无进位相加来理解更容易记住)
3)异或运算满足交换律和结合律(记住这句话,就是同一批数不用管顺序,异或起来的结果都是一样的

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

N & ((~N) + 1)

怎么把一个int类型的数,提取出最右侧的1来(其余所有位置都为0)?这是一个常规操作,需要记住。原因看截图:

在这里插入图片描述

题目

例1:一个数组中只有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这个数并打印

public static int findNumber(int[] arr) {
	int eor = 0;
	for (int i : arr) {
		eor = eor ^ i;
	}
	return eor;
}

例2:一个数组中有两种数出现了奇数次,其他数出现了偶数次,怎么找到并打印这两种数

我们还是准备一个 eor,在 arr 中不妨设 a 和 b 出现了奇数次,其他数都出现了偶数次。那么我把所有数异或起来的结果一定为一个a异或一个b:eor = a^b。因为剩下的偶数个数都不干扰,都抵消掉了。而因为 a 和 b 不相等,所以 eor 肯定不等于0,也就是说名 eor 一定有位置上有1。假设最右边那个为1的位置为第8位,则 a 的第八位和 b 的第八位一定是不同的。所以整个数组就可以分为两大类,一类为第八位为0的,一类为第八位为1的,并且 a 和 b 肯定在两个不同的类别中。再准备一个 eor’,然后对其中一个类别进行例1中的处理,得到 a,那么 b = eor ^ eor’。然后我们只需要把第八位改成最右边为1的那位,就OK。

在这里插入图片描述

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);

	int a = 0;
	for (int i=0; i<arr.length; i++) {
		if (arr[i] & rightOne != 0) {
			^= arr[i];
		}
	}
	System.out.print("a:" + a + " b:" + a^eor);
}
发布了40 篇原创文章 · 获赞 0 · 访问量 383

猜你喜欢

转载自blog.csdn.net/weixin_43780400/article/details/105666631