转载 高效实用的异或操作

异或(XOR)是一种位运算符,相同为0,相异为1

如0^1=1,0^0=0,1^1=0

异或满足交换律、结合律

a^b=b^a

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

a^b^c^d=a^d^c^b

异或是一种位运算,能够高效地巧妙地完成一些功能

1、  实现两个数的交换,swap函数

 

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];
	}

 

 

2、  在一组数中,只有一个数只出现一次,其他数都出现两次,请找出这个只出现一次的数

见到这个问题,第一反应就是依次拿出每一个数字和整个数组比较,若某一数字没有与之相等的元素,则输出这个数字。这是最容易想到也是最笨的方法,算法复杂度为O(n2)

有没有一种只遍历一次数组就可以找出这个数的方法呢。很多时候,我们都会用以空间换时间的方法来降低算法的时间复杂度。于是,对于这个问题,我们又想到遍历一次数组把每个数出现的次数存起来,然后再找出只出现一次的数字。要记录每个数的出现次数,可以用map来实现,键来标识数字,值来标识键在数组中出现的次数。这样遍历一次数组就可以形成一个键值对,然后再遍历这个map,找出值为1的那个键即可。这种方法实现起来也很简单。

在这个数组中既然只有一个数出现一次,其他数都出现两次。根据异或的性质,我们把这组数互相异或一下,相同的数结果为0,最后只剩下出现次数为1的那个数了。

/**
	 * 数组 异或
	 * @param arr
	 * @return 数组中所有数值异或的结果
	 */
	public static int xorArray(int[] arr) {
		int length = 0, res = 0;
		length = arr.length;
		for (int i = 0; i < length; i++) {
			res ^= arr[i];
		}
		return res;
	}

 


 

3、    对上一个问题再提升一个难度,如果在一组数中只有两个数只出现一次,其他数字都出现两次,请找出这两个数字

我们套用上边的思路如果对数组中全部数字异或,最后只得到一个数,这个数是要求的这两个数的异或结果。但是从这个异或结果不能把要求的两个数分离出来。

仔细分析这个异或值,假如这两个数的二进制值分别为001010和001110,异或结果为000100,也就是两个数中不同的位为1。因为这两个数不同,异或结果肯定至少有一位为1。我们任取一位为1的位,依据这一位是否为1把整个数组分成两部分。这样这两个数就分在了不同的组中。然后再对每一个部分按照方法2异或得出结果。

到这里有人可以会有疑问,把数组分割成两部分,如果相同的两个数分在不同的组中结果不就不对了吗。我们分组的依据是某一位是否为1,如果两个数相同,那么这一位也肯定相同,这样两个相同的数肯定会分到同一组中。实现代码如下:

/**
 * 数组中只有两个数不成双,其数字都是成双的,输出这两个数
 * @author 王竹
 */
package com.wangzhu.demo;

import java.io.BufferedInputStream;
import java.util.Scanner;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int[] arr = null;
		int length = -1, res = -1, res1 = -1, res2 = -1, temp = -1;
		while (cin.hasNext()) {
			length = cin.nextInt();
			arr = new int[length];
			for (int i = 0; i < length; i++) {
				arr[i] = cin.nextInt();
			}
			res = xorArray(arr);
			temp = (res ^ ((res - 1) & res));//获得res二进制中最右的1
			res1 = 0;
			res2 = 0;
			for (int i = 0; i < length; i++) {
				if ((temp & arr[i]) == 0) {
					res1 ^= arr[i];
				} else {
					res2 ^= arr[i];
				}
			}
			for (int i : arr) {
				System.out.print(i + " ");
			}
			System.out.println("\n" + res1 + " " + res2);
		}

	}

	/**
	 * 数组 异或
	 * @param arr
	 * @return 数组中所有数值异或的结果
	 */
	public static int xorArray(int[] arr) {
		int length = 0, res = 0;
		length = arr.length;
		for (int i = 0; i < length; i++) {
			res ^= arr[i];
		}
		return res;
	}
}

 

 

异或是一种位操作,实现起来非常高效 

posted @ 2012-07-27 16:59  qingyezhu  阅读(292)  评论(0编辑  收藏  举报