leetcode 260. Single Number III

来源:互联网 发布:洗数据 编辑:程序博客网 时间:2024/06/10 01:40

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

For example:

Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].

Note:

  1. The order of the result is not important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
我一开始想用那个找唯一数的翻转办法,结果发现测试用例中有[-1139700704,-1653765433],这样的话需要初始化的数组真是太大了,得不偿失,只能用最原始的map做。

public class Single_Number_III_260 {public int[] singleNumber(int[] nums) {int[] result=new int[2];int pointer=0;Map<Integer,Integer> map = new HashMap<Integer,Integer>();for(int i=0;i<nums.length;i++){if(map.get(nums[i])==null){map.put(nums[i], 1);}else{map.put(nums[i], -1);}}Iterator iter = map.entrySet().iterator();while (iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();Object key = entry.getKey();Object val = entry.getValue();if((int)val==1){result[pointer]=(int)key;pointer++;}}return result;}public static void main(String[] args) {// TODO Auto-generated method stubSingle_Number_III_260 s=new Single_Number_III_260();int[] nums1=new int[]{-1139700704,-1653765433};System.out.println(Arrays.toString(s.singleNumber(nums1)));}}
大神的方法是使用异或,但是有2个不同的数,怎么办呢?这就需要两次异或遍历:
在第一趟遍历中,我们挨个异或数组中的所有数,最后得到的结果是那2个不同数的异或值。因为这2个数不一样,所以这个值的bit中一定存在1位。然后找到这个值中的最右1,设这个最右1位于i位上。
在第二趟遍历中,我们将数组中所有数分为2组,一组是在i位上为0的数,一组是在i位上为1的数。然后分别对这2组数求异或,就能够分别得到不同的那两个数了。英文版如下:

Once again, we need to use XOR to solve this problem. But this time, we need to do it in two passes:

  • In the first pass, we XOR all elements in the array, and get the XOR of the two numbers we need to find. Note that since the two numbers are distinct, so there must be a set bit (that is, the bit with value '1') in the XOR result. Find
    out an arbitrary set bit (for example, the rightmost set bit).

  • In the second pass, we divide all numbers into two groups, one with the aforementioned bit set, another with the aforementinoed bit unset. Two different numbers we need to find must fall into thte two distrinct groups. XOR numbers in each group, we can find a number in either group.

这个算法的重点在于,这2个需要找出来的数,设为a和b,因为它们不相同,所以他们的bit一定存在某个位置上a为0,b为1,或者a为1,b为0。所以两者异或的值的bit中一定存在1。if some bit of the XOR result is 1, it means that the two target numbers differ at that location.
Let’s say the at the ith bit, the two desired numbers differ from each other. which means one number has bit i equaling: 0, the other number has bit i equaling 1.
Thus, all the numbers can be partitioned into two groups according to their bits at location i.
  • the first group consists of all numbers whose bits at i is 0.
  • the second group consists of all numbers whose bits at i is 1.
Notice that, if a duplicate number has bit i as 0, then, two copies of it will belong to the first group. Similarly, if a duplicate number has bit i as 1, then, two copies of it will belong to the second group.
  • by XoRing all numbers in the first group, we can get the first number.
  • by XoRing all numbers in the second group, we can get the second number.

下面是解决代码:

public int[] singleNumber(int[] nums) {    int result[] = new int[2];            int xor = nums[0];    for (int i=1; i<nums.length; i++)    {        xor ^= nums[i];    }        int bit = xor & ~(xor-1);    int num1 = 0;    int num2 = 0;        for (int num : nums)    {        if ((num & bit) > 0)        {            num1 ^= num;        }        else        {            num2 ^= num;        }    }        result[0] = num1;    result[1] = num2;    return result;}
解法中使用xor & ~(xor-1)获取异或bit中的最右1,如0110的话,结果就是0010。
至于为什么这样做我也不太清楚,但是你看,
xor=011时,xor-1=010,~(xor-1)=101,当011和101做与操作时,正好剩下最后一位的1。所以xor为奇数时,这个很容易理解。
xor=0110时,xor-1=0101,~(xor-1)=1010,当0110和1010做与操作时,也正好剩下最后一位的1。当xor为偶数时也能验证正确。


原创粉丝点击