异或的性质

异或是一种<mark>基于二进制的位运算</mark>,用<mark>符号XOR或者 ^</mark> 表示

其运算法则是对运算符两侧数的每一个二进制位,<mark>同值取0,异值取1</mark>。

它与布尔运算的区别在于,当运算符两侧均为1时,布尔运算的结果为1,异或运算的结果为0。

<mark>简单理解就是不进位加法,如1+1=0,,0+0=0,1+0=1。</mark>

性质

  1. <mark>交换律</mark> 可任意交换运算因子的位置,结果不变
  2. 结合律(即(a^ b )^c ==a ^ (b ^ c))
  3. 对于任何数x,都有<mark>x^x=0</mark>,<mark>x ^ 0=x</mark>,同自己求异或为0,同0求异或为自己
  4. <mark>自反性 A ^ B ^ B = A ^ 0 = A ,连续和同一个因子做异或运算,最终结果为自己</mark>( <mstyle mathcolor="&#35;ff0011"> </mstyle> \color{ff0011}{最重要的性质}
  5. <mark>(保留信息) A^B 之后,只要有 A 或者 B 都肯定能算出另外一个数。</mark>

例一:不引入第三个变量,交换两个变量(整数)的值

// 假设存在 a b 两个整数

a = a^b

b = a^b

a = a^b

是不是很神奇,如果不理解,请仔细推算,推算完后脑洞会开一点吧。


例二:判断奇偶数更简单高效的做法

奇数二进制的最低位一定是1,偶数二进制的最低位一定是0,所以 a^1==1?偶数:奇数 ,代码就不写了吧。。。脑洞再开

和0做与运算也是可以的a&0==0?偶数:奇数

Java测试:

int a,b ,c ;
		System.out.println(a = 0b1111); // 15
		System.out.println(b = a^0b1); // 14 1110 
		System.out.println(c = a^1); // 14 1110
		int d ;
		System.out.println(d=15^1);
		System.out.println(d&1);
		System.out.println("----判断偶数---");
		for(int i=0 ; i<10 ; i++) {
			if(((i^0)&1) == 0 ) {
				System.out.println(i);
			}
		}


例三:找出那个唯一成对的数

1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现 一次。
每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空 间,能否设计一个算法实现?

  • 解法一:显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去(1+2+…+1000)的和。 这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。

  • 解法二:异或就没有这个问题,并且性能更好。
    将所有的数全部异或(1 ^ 2 ^ … ^ 1000 ^ k,k为重复数),得到的结果与(1 ^ 2 ^ 3 ^ … ^ 1000)的结果进行异或,得到的结果就是重复数。 即(1 ^ 2 ^ … ^ 1000 ^ k ) ^ (1 ^ 2 ^ 3 ^ … ^ 1000),由于有结合律、交换律存在,我们可以这么调整为(1 ^ 1 ^ 2 ^ 2 ^ … . ^ 1000 ^ 1000 ^ k)那结果当然就是k了,哈哈哈

Java测试:

int n = 1001 ;
		//1000个数中,随机插入一个重复的。
		int ran = new Random().nextInt(n);
		ArrayList<Integer> list = new ArrayList<Integer>(n);
		for(int i=1 ; i<n ; i++) {
			list.add(i);
			if(i==ran){
				list.add(ran);
			}
		}
		//取得那个数
		int num = 0 ; 
		for(int i=0 ; i<list.size() ; i++) {
			num = num^list.get(i)^i;
		}
		System.out.println("猜测:"+num);
		System.out.println("答案:"+ran);


例四:上例变形,找出出现奇数次的数

google面试题的变形:

  • 一个数组存放若干整数,其中一个数出现奇数次,其余数均出现偶数次。

找出这个出现奇数次的数?

结果=a[0] ^ a[1] ^ . . . ^ a[n-1]

因为奇数个数字连续异或之后是自己,偶数个数连续异或后为0,自己和0异或还是自己。。。真的很神奇


例六:突然变难,找出数组中落单的两个数

题目:

  • 一个整型数组里除了两个数字之外,其他的数字都出现了两次。

<mark>请写程序找出这两个只出现一次的数字。</mark>
<mark>要求时间复杂度是O(n),</mark>
<mark>空间复杂度是O(1)。</mark>

java测试:

//有两个落单的数,用异或找出来
		//3和7落单
		Integer[] a = new Integer[]{1,9,2,5,3,4,8,5,2,6,6,7,4,8,9,1};
		
		Integer value = new Integer(0);
		for (Integer i : a) {
			
		}
		

这里加了代码实现,
原文章《异或的用途》:
https://blog.csdn.net/qq_39705793/article/details/81237005?from=singlemessage