题目
思路分析
看到这道题的时候,脑子里闪过两种解法:
- 哈希表 因为题目给的O(n)时间复杂度,所以第一秒想到的解法就是开哈希表,遍历nums记录每个数字对应的出现次数,再遍历哈希表找到出现次数为1的两个数字。但是这种解法空间复杂度远超O(1),pass
- 排序 既然哈希空间复杂度不够,那么就会想到用排序的做法,先把nums按从小到大进行排序,接下来只要遍历一次排序后的nums,找到没有连续出现2次的数字就行了。这下,空间复杂度确实是O(1)了,但是最快的排序时间复杂度也要O(nlogn),又变成时间复杂度不符合要求了,pass
思路到此中断。再仔细看这道题,觉得有点眼熟,想起来之前做过一道题是找出数组中只出现1次的1个数字,而这道题要求的是找出只出现1次的2个数字。前者对应leetcode 136. 只出现一次的数字,这题的标准解法是利用相同的数字异或之后的结果一定是0,对数组中所有的数字进行异或,最后得到的结果就是只出现1次的那个值,此时时间复杂度为O(n),空间复杂度O(1),刚好满足题目要求。本题中,如果将所有数字都进行异或,最后得到的结果就是只出现1次的2个值的异或,这两个数的异或可能是任何值,乍一看这个异或操作没有任何意义。但是由于一定存在2个只出现1次的值,即这两个值一定不相同,因此异或的结果一定不为0,最终结论是:这两个数一定存在某个比特位不相等。 根据这一点,可以得到最终符合题目要求的解法(以示例1为例):
- 将nums中所有数字进行异或,得到2个只出现1次的数字的异或值:
4 ^ 1 ^ 4 ^ 6 = 1 ^ 6 = 7 = 111 - 取异或值7中为1的位idx(如:取最低位,idx = 0),2个目标数最低位一定不相等
- 将nums中的数字按该位是否为1分成2组,则这两个只出现1次的数一定不在同一组,此时组1:4,4,6;组2:1
- 现在问题转变为对这两个数组,分别完成leetcode 136. 只出现一次的数字的操作,即分别对这两个数组中的数再做异或,得到的两个数即为2个目标值
组1:4 ^ 4 ^ 6 = 6;组2:1,即答案为[6, 1]
代码
int* singleNumbers(int* nums, int numsSize, int* returnSize){
int *res = (int *)malloc(sizeof(int) * 2);
*returnSize = 2;
if (numsSize == 2) {
return nums;
}
int orNum = 0;
for (int i = 0; i < numsSize; i++) {
orNum ^= nums[i];
}
int idx = 0;
while((orNum & 1) == 0) {
orNum = orNum >> 1;
idx++;
}
int orNum0 = 0;
int orNum1 = 0;
for(int i = 0; i < numsSize; i++) {
if ((nums[i] >> idx) & 1) {
orNum0 ^= nums[i];
} else {
orNum1 ^= nums[i];
}
}
res[0] = orNum0;
res[1] = orNum1;
return res;
}