BM52 数组中只出现一次的两个数字¶
中等通过率 52.12%python3核心代码模式样例通过牛客 AC哈希位运算
讲解章节:位运算
一句话
全体异或得到 a 异或 b,用它的最低位 1 把数组分成两组。
解题思路¶
这题考什么¶
异或的两条性质:x 异或 x = 0,0 异或 y = y。 若只有一个数落单,全体异或就是答案;现在落单的有两个,全体异或得到的是 xor_all = a 异或 b,还差一步把它们分开。
分组依据:a 与 b 不相等,所以 xor_all 不为 0,它至少有一个二进制位是 1, 而某一位为 1 恰恰说明 a 与 b 在这一位上不同(一个 0 一个 1)。 取最低位的 1 最省事:
补码下 -x 等于 x 按位取反再加一,与原数相与只剩最低位的那个 1。 按「这一位是不是 1」把数组切成两组:
于是每组都变成「一个落单 + 若干成对」,各自全体异或就得到 a 与 b。 位运算的基本技巧见 位运算。
数据规模与复杂度¶
2 <= n <= 1000,元素取值 0 到 1e6,时限其他语言 2 秒。 扫两遍数组,时间 O(n);只用几个整数变量,空间 O(1)。两项都是题面的要求。 哈希计数同样是 O(n) 时间,但要开一张 O(n) 的表,不满足空间要求。
坑在哪¶
- 输出必须非降序(题面提示原文「输出时按非降序排列」)。 两个落单的数分别落在哪一组完全由数据决定,直接返回分组顺序有一半的概率是逆序, 判题严格比对,顺序错就是错。返回前比一次大小即可。
- 取的必须是 xor_all 里某个为 1 的位。写成 xor_all & (xor_all - 1) 是「清掉最低位的 1」,含义正相反,用它分组会把 a 与 b 分到同一组, 两组的异或结果一个是 a 异或 b、一个是 0。
- 分组的正确性依赖「其余数字恰好各出现两次」这个前提。若某个数出现三次, 它在组内抵消不掉,结果直接失效。
- 判断分组时写 x & diff 是「该位为 1」,取值是 diff 而不是 1, 所以不能拿它和 1 比较,只能当布尔值用。
样例复核¶
[1, 4, 1, 6]。
xor_all = 1 异或 4 异或 1 异或 6 = 4 异或 6 = 2,二进制 010
diff = 2,即第 1 位(从 0 数起)
6 的二进制 110,与 diff 相与得 2,进 a 组
4 的二进制 100,与 diff 相与得 0,进 b 组
两个 1 的二进制 001,都进 b 组并互相抵消
a = 6,b = 1 异或 4 异或 1 = 4
非降序输出 [4, 6],与样例一致——不排序就会输出 [6, 4]。
参考实现¶
[:octicons-arrow-left-16: BM51](BM51.md) [BM53 :octicons-arrow-right-16:](BM53.md)