lintcode84- Single Number III- medium
Given 2*n + 2 numbers, every numbers occurs twice except two, find them.
Given [1,2,2,3,4,4,5,3] return 1 and 5
全异或剩两数temp=a^b,temp &= -temp, 用if(temp & A[i] == 0)的方法把所有数分为两堆,ab必定被分开在两堆中,堆内全异或即结果。
解题思路(转自http://www.cnblogs.com/zihaowang/p/5241825.html):
这道题可以用HashMap来做,但是对于空间复杂度要求很高。题目要求的是常量的空间复杂度;
参考网上别人的思路,发现了一种非常巧妙的方法;
首先遍历整个数组,讲所有的值相异或,那么最后的值肯定是两个相异的值异或的结果。
这两个值不同,那么肯定有一位的二进制值不同,那么这个位相异或的结果肯定是这位的数值为1;
那么我们寻找这个数值位为1的位,
这里采用非常巧妙的方法:resTwo &= -resTwo; 因为int整数在java中是按照补码的方式来的,那么正数和它负值按位与的结果是原始最右边非0位的数字为1,其余位都为0;
这样我们把原来的数组分为两个部分,一部分是和resTwo按位与的结果为0的,另一部分的结果和resTwo按位与的结果为1的,并且那两个不相等的数分别落在这两个组中;
这样分别对两组的数异或,即可得到这两个数。
public class Solution { /* * @param A: An integer array * @return: An integer array */ public List<Integer> singleNumberIII(int[] A) { // write your code here int together2 = 0; int first = 0; int second = 0; //list 和arraylist什么区别? for (int i = 0; i < A.length; i++){ together2 ^= A[i]; } together2 &= -together2; for (int i = 0; i < A.length; i++){ if ((A[i] & together2) == 0){ first ^= A[i]; } else { second ^= A[i]; } } List<Integer> res = new ArrayList<Integer>(); res.add(first); res.add(second); return res; } }
1.temp &= -temp 做的是让从右边开始数第一位为1的位保留,其他全变0。如a 原码 00001010 补码与原码相等 00001010。负数补码: a的反码 11110101 最后+1:11110110 。两者&的话就一个最右边的1留下。)
2.temp能分开两数是因为一开始a^b如果某一位为1,那说明肯定a和b在这一位表现是不同的。你把这位定出来得temp以后,之后这个temp&a和temp&b肯定要么是0要么不是0!
3. 小心==的优先级是比&高的!!(想想平时先判断== 再判断&&的,这里&有逻辑处理也有位处理,跟&&更近的!)所以if ((A[i] & together2) == 0)里的括号千万不能少。