找到独特的数字卡片——位运算巧解重复数字问题
在一个班级中,每位同学都拿到了一张数字卡片。除了一个数字外,所有的数字都恰好出现了两次。我们的任务是帮助班长小C找到那个唯一没有配对的数字。
一、问题分析
这个问题看似简单,但要在时间复杂度为 O(n) 和空间复杂度为 O(1) 的条件下解决,需要一些巧妙的思路。
1. 重复数字的特性
- 成对出现:除了一个数字外,其余数字都出现了两次。
- 唯一性:需要找到那个只出现一次的数字。
2. 常规解法的局限
- 使用哈希表:可以记录每个数字出现的次数,但空间复杂度为 O(n)。
- 排序后比较:时间复杂度为 O(n log n),不满足要求。
为满足题目要求,我们需要一种既能在 O(n) 时间内解决,又能将空间复杂度降到 O(1) 的方法。
二、解题思路
1. 利用异或运算
异或(XOR)操作的性质
- 交换律:a ^ b = b ^ a
- 结合律:a ^ (b ^ c) = (a ^ b) ^ c
- 自反性:a ^ a = 0
- 零元性:a ^ 0 = a
异或操作的应用
- 成对抵消:两个相同的数字异或后为 0。
- 累积结果:将所有数字异或,最终结果就是那个唯一的数字。
2. 算法步骤
- 初始化结果变量:设定一个变量
uniqueNumber = 0。 - 遍历数组并异或:对于数组中的每个数字
num,执行uniqueNumber ^= num。 - 返回结果:遍历结束后,
uniqueNumber就是那个唯一的数字。
三、代码实现
public class Main {
public static int solution(int[] cards) {
int uniqueNumber = 0;
for (int num : cards) {
uniqueNumber ^= num; // 异或操作,抵消成对的数字
}
return uniqueNumber;
}
public static void main(String[] args) {
// 测试样例1
int[] cards1 = {1, 1, 2, 2, 3, 3, 4, 5, 5};
System.out.println("唯一的数字是:" + solution(cards1)); // 输出:4
// 测试样例2
int[] cards2 = {0, 1, 0, 1, 2};
System.out.println("唯一的数字是:" + solution(cards2)); // 输出:2
// 测试样例3
int[] cards3 = {7, 3, 3, 7, 10};
System.out.println("唯一的数字是:" + solution(cards3)); // 输出:10
}
}
四、详细解释
1. 初始化结果变量
int uniqueNumber = 0;
- 初始值为 0,因为 0 与任何数异或结果为该数本身。
2. 遍历数组并异或
for (int num : cards) {
uniqueNumber ^= num;
}
- 循环过程:依次将数组中的每个数字与
uniqueNumber进行异或操作。 - 异或结果更新:由于成对的数字会互相抵消,最终
uniqueNumber中只会剩下那个唯一的数字。
3. 返回结果
return uniqueNumber;
- 最终的
uniqueNumber就是我们要找的数字。
五、示例演示
示例1:cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
| 步骤 | num | uniqueNumber 前 | 异或操作 | uniqueNumber 后 |
|---|---|---|---|---|
| 1 | 1 | 0 | 0 ^ 1 = 1 | 1 |
| 2 | 1 | 1 | 1 ^ 1 = 0 | 0 |
| 3 | 2 | 0 | 0 ^ 2 = 2 | 2 |
| 4 | 2 | 2 | 2 ^ 2 = 0 | 0 |
| 5 | 3 | 0 | 0 ^ 3 = 3 | 3 |
| 6 | 3 | 3 | 3 ^ 3 = 0 | 0 |
| 7 | 4 | 0 | 0 ^ 4 = 4 | 4 |
| 8 | 5 | 4 | 4 ^ 5 = 1 | 1 |
| 9 | 5 | 1 | 1 ^ 5 = 4 | 4 |
- 最终结果:
uniqueNumber = 4。
示例2:cards = [0, 1, 0, 1, 2]
- 过程类似,最终
uniqueNumber = 2。
六、时间和空间复杂度分析
1. 时间复杂度:O(n)
- 原因:只需遍历一次数组,执行 n 次异或操作。
2. 空间复杂度:O(1)
- 原因:只使用了一个额外的变量
uniqueNumber,不依赖于输入规模。
七、扩展思考
1. 为什么异或能找到唯一的数字?
- 异或的抵消特性:相同的数字异或后为 0,不同的数字异或后保留信息。
- 累积结果:经过异或操作,所有成对的数字被抵消,只剩下那个唯一的数字。
2. 异或操作的其他应用
- 交换两个变量而不使用临时变量:
a = a ^ b; b = a ^ b; a = a ^ b; - 检测两个数的奇偶性是否相同:
(a ^ b) & 1。
3. 如果数字出现的次数不同怎么办?
- 如果其他数字出现的次数不是两次,而是三次、四次,或者出现次数不定,那么需要使用其他算法,例如位运算统计每一位上的 1 的个数。
八、总结
通过巧妙地利用异或运算的性质,我们在满足时间和空间要求的情况下,成功地找到了数组中唯一不重复的数字。这种位运算的方法在处理成对出现的问题时,非常高效且简洁。
- 优势:算法简洁,易于实现,性能高效。
- 适用范围:适用于数组中除了一个数字外,其他数字都出现偶数次的情况。
注意事项
- 数据范围:确保数字在整数范围内,避免溢出。
- 特殊情况:如果数组为空或不满足题目条件,需要进行异常处理。