Leetcode ☞ 260. Single Number III ☆☆☆

网址:https://leetcode.com/problems/single-number-iii/


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?

我的AC:

/**
 * Return an array of size *returnSize.
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumber(int* nums, int numsSize, int* returnSize) {
    if (!nums || (numsSize < 2))
        return NULL;
        
    *returnSize = 2;    
    int* ans = (int*)calloc(2,sizeof(int));
    
    if (ans) {
        int a = 0, b = 0, flag, i;//a,b为特殊的那两个数。
        
        for (i = 0; i < numsSize; i++){
            a ^= nums[i];   //此时的a = a^b
        }
        flag = a & -a; //找出a^b的为1的最低位。https://leetcode.com/discuss/52377/3-lines-ruby-4-lines-c
        
        for (i = 0; i < numsSize; i++){
            if (nums[i] & flag)//a&flag == 0,b&flag != 0,所以循环过后b=b。
                b ^= nums[i];
        }
        ans[0] = a ^ b;
        ans[1] = b;

    }
    
    return ans;
}


解题思路:

现在这个数组中,有两个数只出现了一次,求出这两个数字,且时间、空间复杂度不变。

如果根据single number的解法,我们最后也是得到了一个值,但这个值是那两个数字(只出现一次)的异或值。我们又不知道这个两个数字的任何一个,所以我们得不到这两个数字。


如果我们把数组分割成2个数组,每个数组中只含有一个只出现一次的数字,再调用single number的解法,我们就能得到结果。关键在于我们如何分割这个数组,且空间复杂度是O(1)?

首先我们从头到尾依次异或数组中的每一个数字,得到的结果就是两个只出现一次数字的异或结果,因为这两个数字肯定不一样,所以得到的结果数字肯定不是0,也就是这个二进制数字中至少有一位是1 , 我们在这个结果数字中任找一个为 1的位的位置,我们就通过这一位是不是1,来把原始数组分割成两个数组。


代码思路:

  1. x = 数组中每个数的异或 ==> x = a ^ b
  2. ∵a != b   ∴x里至少有一位为1
  3. 【关键】取x里为1的任意一位(即ab在这一位是不同的,即ab一个在这位为1,另一个在这位为0),称其为flag
  4. 循环,让flag和数组中每个数进行与运算,结果不为0的数就跟初始化为0的b进行异或运算。(此步中,ab中只有一个数满足if判断条件,因此筛出)
  5. a = x ^ b

如何找上述的flag:

flag = x ^ -x

原理:

在计算机中,负数以其正值的补码形式表达
原码一个整数,按照绝对值大小转换成的二进制数,称为原码。
反码将二进制数按位取反,所得的新二进制数称为原二进制数的反码。

补码反码加1,称为补码。

假设有一int类型的数,值为5,

那么,我们知道它在32位计算机中表示为:00000000 00000000 00000000 00000101,

所以,5的反码是11111111 11111111 11111111 11111010,

所以,-5 在计算机中表达为:11111111 11111111 11111111 11111011。


任意一个数x,和其相反数的与,x ^ -x,能找出x中为1的最低的那一位。 5 ^ -5 = 00000000 00000000 00000000 00000001。

符合flag的要求:为1的任意一位。

         


参考如下两个链接:

https://leetcode.com/discuss/52369/c-o-n-time-o-1-space-7-line-solution-with-detail-explanation

https://leetcode.com/discuss/52377/3-lines-ruby-4-lines-c



你可能感兴趣的:(Leetcode ☞ 260. Single Number III ☆☆☆)