利用異或(XOR)運算的性質:
a ^ 0 = aa ^ a = 0將數組所有元素依次異或,成對出現的數字抵消為 0,最終結果即為那個唯一的數字。
int findSingle(int arr[], int n) {
int res = 0;
for (int i = 0; i < n; ++i) {
res ^= arr[i];
}
return res;
}
時間複雜度 O(n),空間複雜度 O(1)。
思路:將數組分為兩組,使得這兩個唯一數字分落在不同組內,每組內其餘數字仍成對出現,轉化為問題 1 求解。
xorAll = a ^ b(a、b 為兩個唯一數字)。xorAll 中至少有一位為 1(因為 a ≠ b)。取 xorAll 的最低位 1 作為分組標誌:mask = xorAll & (-xorAll)。(num & mask) != 0 分為兩組,分別異或,即得到 a 和 b。void findTwoSingles(int num[], int n) {
int xorAll = 0;
for (int i = 0; i < n; ++i) xorAll ^= num[i];
int mask = xorAll & (-xorAll); // 取最低位 1
int a = 0, b = 0;
for (int i = 0; i < n; ++i) {
if (num[i] & mask) a ^= num[i];
else b ^= num[i];
}
printf("%d %d\n", a, b);
}
x & -x 為何能取最低位 1?負數在計算機中以補碼表示:-x = ~x + 1。
最低位 1 及其右側所有位在 x 和 -x 中相反,左側相同。因此 x & -x 只保留最低位 1。
例:4 (0100) & -4 (1100) = 0100;1000 (1111101000) & -1000 = 1000。
| 方法 | 時間複雜度 | 空間複雜度 | 備註 |
|---|---|---|---|
| 異或單遍歷 | O(n) | O(1) | 僅適用於單一唯一數字 |
| 分組異或(數組輔助) | O(n) | O(n) | 易理解,但需額外空間 |
| 分組異或(原地,推薦) | O(n) | O(1) | 最優解,利用 x & -x 技巧 |
位運算是處理「成對抵消」類問題的利器,掌握 x & -x 取最低位 1 的技巧,能輕鬆解決多數字唯一值查找問題。