【位运算】【数组】【2023-10-16】
260. 只出现一次的数字 III
找出数组中恰好只出现一次的连个元素,其余的所有元素均出现两次。要求算法的时间复杂度为线性的,空间复杂度为常量级别的。
我们先对数组 nums
中的虽有元素进行一次异或操作,得到的值 x
是数组中仅仅出现过一次的两个数的异或值,即 x = x 1 ⨁ x 2 x = x_1 \bigoplus x_2 x=x1⨁x2, x 1 x_1 x1 和 x 2 x_2 x2 分别表示数组中仅仅出现过一次的两个数。
显然,这个 x x x 的值不会等于 0
,因为一旦等于 0
,那么说明 x 1 = x 2 x_1 = x_2 x1=x2,这样 x 1 x_1 x1 和 x 2 x_2 x2 就不是仅出现一次的数了。
现在使用位运算 KaTeX parse error: Expected 'EOF', got '&' at position 3: x &̲ -x 可以得到 x x x 的二进制表示位中最低位的 1
,设其为第 i
,也就表示 x 1 x_1 x1 和 x 2 x_2 x2 中的某一个数的二进制表示的第 i
位为 0
,另一个数的第 i
位为 1
,我们可以据此来将数组中的所有元素分类:
i
位为 1
的为一类;i
位为 0
的为一类;根据以上分类,我们知道 x 1 x_1 x1 和 x 2 x_2 x2 分属在以上两个类中。每个类中出现的数有一个数是仅出现一次的数,其他的数都是出现两次的。于是,我们对每一个类中的元素进行异或和操作,就得到了该类中仅出现一次的元素。对这两个类分别进行异或和操作就得到了数组 nums
中仅出现了一次的两个数。
实现代码
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int sum = 0;
for(auto &num : nums){
sum ^= num;
}
// 防止溢出
int lst = ((sum == INT_MIN) ? sum : sum & (-sum));
int num1 = 0, num2 = 0;
for(auto &num : nums){
if(lst & num){
num1 ^= num;
}
else{
num2 ^= num;
}
}
return {num1, num2};
}
};
复杂度分析
时间复杂度: n n n 是数组 nums
的长度。
空间复杂度: O ( 1 ) O(1) O(1)。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* singleNumber(int* nums, int numsSize, int* returnSize){
int xorsum = 0;
for (int i = 0; i < numsSize; ++i) {
xorsum ^= nums[i];
}
int lsb = (xorsum == INT_MIN ? xorsum : xorsum & (-xorsum));
int x1 = 0, x2 = 0;
for (int i = 0; i < numsSize; ++i) {
int num = nums[i];
if (num & lsb) {
x1 ^= num;
}
else {
x2 ^= num;
}
}
int *res = (int *)malloc(sizeof(int) * 2);
res[0] = x1;
res[1] = x2;
*returnSize = 2;
return res;
}
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
xorsum = 0
for num in nums:
xorsum ^= num
lsb = xorsum & (-xorsum)
x1, x2 = 0, 0
for num in nums:
if num & lsb:
x1 ^= num
else:
x2 ^= num
return [x1, x2]
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 哦。