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)里的括号千万不能少。 
原文地址:https://www.cnblogs.com/jasminemzy/p/7510483.html