网址: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:
[5, 3]
is also correct.我的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,来把原始数组分割成两个数组。
代码思路:
如何找上述的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