这个题目要求我们找出数组中仅出现一次的两个数字,其他数字都恰好出现两次。位运算解法之所以高效,是因为它利用了异或运算的几个关键特性:
当我们对整个数组进行异或操作时,出现两次的数字会相互抵消(结果为0),最终剩下的就是两个只出现一次的数字的异或结果。
假设这两个唯一的数字是a和b,整个数组异或的结果就是a ^ b。这个结果中为1的位表示a和b在该位上不同。我们可以利用这个特性来区分a和b:
python复制def singleNumber(nums):
# 第一步:得到a^b
xor_result = 0
for num in nums:
xor_result ^= num
# 第二步:找到最右边的1
mask = 1
while (xor_result & mask) == 0:
mask <<= 1
# 第三步:分组并分别异或
a, b = 0, 0
for num in nums:
if num & mask:
a ^= num
else:
b ^= num
return [a, b]
原解法中使用循环来寻找最右边的1,这可以通过位运算技巧优化:
python复制mask = xor_result & -xor_result
这个技巧利用了补码表示的特性,可以一步得到最右边的1。
这个问题可以推广到更一般的情况:数组中所有数字都出现k次,只有一个数字出现m次(m ≠ k)。对于这种情况,我们可以使用位计数的通用解法:
虽然这种通用解法时间复杂度较高(O(n*32)),但它展示了位运算解法的扩展性。
当数组中包含负数时,位运算解法仍然有效,因为Python中的整数是补码表示,位运算的行为是一致的。
位运算解法的时间复杂度是O(n),空间复杂度是O(1),非常适合处理大规模数据。在实际测试中,对于包含100万个元素的数组,Python实现也能在毫秒级完成计算。
哈希表解法需要O(n)的空间复杂度,虽然时间复杂度也是O(n),但在空间使用上不如位运算解法高效。
可以利用数学公式:
2*(a+b) - sum(nums) = a + b
结合a^b可以解出a和b,但这种方法可能面临整数溢出的问题。
这种位运算技巧在以下场景中特别有用:
提示:理解位运算的关键是多做练习,尝试手工计算一些小例子,观察位模式的变化规律。
位运算符的优先级有时会带来意想不到的结果。例如:
python复制if num & mask == 0: # 错误!等同于 num & (mask == 0)
if (num & mask) == 0: # 正确
虽然Python中负数也能正确处理,但在某些语言中右移负数可能导致问题。建议使用无符号右移(如果语言支持)。
好的测试用例应该包括:
对于特别大的数组,可以考虑以下优化:
这个问题可以引出许多有趣的变种:
每种情况都需要不同的位运算策略,是很好的算法练习题目。
在实际工程中,位运算常用于:
理解并掌握位运算技巧,可以让你写出更高效、更优雅的代码。