异或运算的应用

异或的运用

Unique-You 2018-03-22 13:31:36   1654   收藏 16
展开
异或是一种基于二进制的位运算,用符号XOR或者 ^ 表示,其运算法则是对运算符两侧数的每一个二进制位,同值取0,异值取1。它与布尔运算的区别在于,当运算符两侧均为1时,布尔运算的结果为1,异或运算的结果为0。

    异或的性质:

    1、交换律:a^b = b^a;

    2、结合律:(a^b)^c = a^(b^c);

    3、对于任意的a:a^a=0,a^0=a,a^(-1)=~a。

    了解了上面这些,来看看这个,很重要,后面的程序都要用到这个结论:

    对于任意的a,有a^b^c^d^a^k = b^c^d^k^(a^a) = b^c^d^k^0 = b^c^d^k,也就是说,如果有多个数异或,其中有重复的数,则无论这些重复的数是否相邻,都可以根据异或的性质将其这些重复的数消去,具体来说,如果重复出现了偶数次,则异或后会全部消去,如果重复出现了奇数次,则异或后会保留一个。

下面来看三道题目:

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

    当然,这道题,可以用最直观的方法来做,将所有的数加起来,减去1+2+3+...+1000的和,得到的即是重复的那个数,该方法很容易理解,而且效率很高,也不需要辅助空间,唯一的不足时,如果范围不是1000,而是更大的数字,可能会发生溢出。

    我们考虑用异或操作来解决该问题。现在问题是要求重复的那个数字,我们姑且假设该数字式n吧,如果我们能想办法把1-1000中除n以外的数字全部异或两次,而数字n只异或一次,就可以把1-1000中出n以外的所有数字消去,这样就只剩下n了。我们首先把所有的数字异或,记为T,可以得到如下:

T = 1^2^3^4...^n...^n...^1000 = 1^2^3...^1000(结果中不含n)

    而后我们再让T与1-1000之间的所有数字(仅包含一个n)异或,便可得到该重复数字n。如下所示:

T^(a^2^3^4...^n...^1000) = T^(T^n) = 0^n = n

2、一个数组中只有一个数字出现了一次,其他的全部出现了两次,求出这个数字。

    明白了上面题目的推导过程,这个就很容易了,将数组中所有的元素全部异或,最后出现两次的元素会全部被消去,而最后会得到该只出现一次的数字。

    该题目同样可以该为如下情景,思路是一样的:数组中只有一个数字出现了奇数次,其他的都出现了偶数次。


3、问题:一个1~n的自然数的乱序数组,其中缺失了1到n之间的某个数,如何快速找出这个数?

 当n不太大时,可以考虑求和。先算出1~n的所有数的和,然后减去数组中出现的所有自然数的和。时间复杂度为O(n),空间复杂度O(1)。这种方法的缺点是n不能太大,n比较大时,求和容易溢出。

用位图。从头到尾的扫描整个数组,把出现的数相应的位设置为1.然后再扫描位图,找出不为1的那一位,即为要找的数。这种方法的时间复杂度为O(n),空间复杂度为O(n)。

可以把1~n的所有数异或,再把数组中出现的所有数异或,然后再把这两个异或的结果异或,最后得到的值即为我们要找的值。这样时间复杂度为O(n),空间复杂度为O(1)。在空间上比第二种方法要好,而且不会出现第一种方法中所说的溢出问题。

------------------------------------------------------------------------------------------------------------------------------------------------------------------

1.一个整形数组,存1到n的所有整数,但缺省一个值,找出此值。
先求1到n的异或,再和数组中所有数异或,结果就是要找的值。

2.一个整形数组除一个数只出现一次外,其余的都出现2次,求这个数
数组中所有值进行异或即可。
3.一个整形数组除两个数只出现一次外,其余的都出现2次,求这两个数。
若求所有数的异或,得出的是那两个数的异或值,这样没法分出两数。
有两种方法:(1)不同的两个数进行异或,肯定有至少1位为1。比如,这两个数的二进制为1101和0001,异或结果为1100。那么找出这个1,就可以把整个数组分为两部分,再利用2中的方法一一找出。

<pre name="code" class="java">public static void findTwoNumber(int[] a) {
    	int sum = 0;
    	for (int i = 0; i < a.length; i++) {
    		sum = sum ^ a[i]; 
		}   
		int bit = 1; // 从最低位,即0001开始找
		while (true) {  
			if ((sum & bit) != 0)  
 	        {  
 	            break;  // 通过与bit相与,找出异或后为1的那一位
 	        }  
 	        else  
 	        {  
 	            bit = (bit<<1);  
 	        }  
 	    }
		int re1 = 0; 
		int re2 = 0;
		for (int i = 0; i < a.length; i++) {
			 if ((bit & a[i]) != 0) { // 利用与bit相与是否为0将数组分成两部分
				 re1 ^= a[i];
			 } else {
				 re2 ^= a[i];
			 }
		 }
		System.out.println(re1 + " " + re2);
	 }

(2)将所有数异或,得出的值为a^b,再一一和数组中的值进行异或,得出的每个值存入一个list,由于两个数之外的值出现两次,判断数组中若已存在此值,将其删掉,最后list中就只有两个值,即为a与b

public static void findTwoNumber(int[] a) {
    	int sum = 0;
    	for (int i = 0; i < a.length; i++) {
    		sum = sum ^ a[i];
    	}
    	List<Integer> list = new ArrayList<Integer>();
    	for (int i = 0; i < a.length; i++) {
    		int u = sum ^ a[i];
    		if (list.contains(u)) {
    			list.remove(Integer.valueOf(u));
    		} else {
    			list.add(u);
    		}
    	}
    	System.out.println((list.get(0)) + " " + (list.get(1)));
	 }

5.一个整形数组除3个数只出现一次外,其余的都出现2次,求这3个数。
将所有数异或,得出的值sum为a^b^c,再一一和数组中的值进行异或,得出的每个值存入一个list。将出现两次的值删掉,最后list中就只有三个值:a^b,a^c,b^c,三值与sum异或,即得c,b,a三个数。同理可求4个或更多出现一次的数。

public static void findThreeNumber(int[] a) {
    	int sum = 0;
    	for (int i = 0; i < a.length; i++) {
    		sum = sum ^ a[i];
    	}
    	List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < a.length; i++) {
    		int u = sum ^ a[i];
    		if (list.contains(u)) {
    			list.remove(Integer.valueOf(u));
    		} else {
    			list.add(u);
    		}
    	}
    	System.out.println((sum ^ list.get(0)) + " " + (sum ^ list.get(1)) + " " + (sum ^ list.get(2)));
   }

6.Leetcode389.Find the Difference

String s 和 String t仅含小写字母字符,其中t是s的重排且加了一个字符,找到那个新加入的字符。题意示例:

Input:

s = "abcd"

t = "abcde"

 

Output:

e
 

 

 

 

 

对s和t的每个字符异或。

public static char findTheDifference(String s, String t) {
		char a = 0;
		for (int i = 0; i < s.length(); i++) {
			a ^= s.charAt(i); // 异或
		}
		for (int i = 0; i < t.length(); i++) {
			a ^= t.charAt(i);
		}
		return a;
	}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值