解析与图解
问题描述
在一个班级中,每个学生都拿到了一张卡片,上面有一个整数。已知除了一个数字外,其他所有数字都恰好出现了两次。目标是设计一个算法,在时间复杂度为 O(n) 且空间复杂度尽可能低的情况下,找到那个只出现一次的数字。
示例分析
让我们先通过示例来理解问题:
-
示例 1:
- 输入:
[1, 1, 2, 2, 3, 3, 4, 5, 5] - 输出:
4 - 解释: 数字
4只出现一次,其他数字都成对出现。
- 输入:
-
示例 2:
- 输入:
[0, 1, 0, 1, 2] - 输出:
2 - 解释: 数字
2只出现一次。
- 输入:
-
示例 3:
- 输入:
[7, 3, 3, 7, 10] - 输出:
10 - 解释: 数字
10只出现一次。
- 输入:
算法思路
为了在 O(n) 的时间复杂度和常数空间复杂度内解决这个问题,我们可以利用位运算中的**异或(XOR)**操作。
异或运算的性质
- 自反性: 任何数与自身异或结果为 0,即
a ^ a = 0。 - 交换律:
a ^ b = b ^ a。 - 结合律:
(a ^ b) ^ c = a ^ (b ^ c)。 - 恒等元: 任何数与 0 异或结果为其自身,即
a ^ 0 = a。
基于这些性质,我们可以得出以下结论:
- 如果一个数组中所有数字都成对出现,那么所有数字异或的结果为 0。
- 如果有一个数字只出现一次,而其他数字都成对出现,那么所有数字异或的结果就是那个只出现一次的数字。
算法步骤
- 初始化一个变量
uniqueNumber为 0。 - 遍历数组中的每个数字
num:- 将
uniqueNumber与num进行异或操作:uniqueNumber ^= num。
- 将
- 遍历结束后,
uniqueNumber中存储的就是只出现一次的数字。
图解
假设输入数组为 [1, 1, 2, 2, 3, 3, 4, 5, 5]:
- 初始
uniqueNumber = 0 - 遍历第一个
1:0 ^ 1 = 1 - 遍历第二个
1:1 ^ 1 = 0 - 遍历第一个
2:0 ^ 2 = 2 - 遍历第二个
2:2 ^ 2 = 0 - 遍历第一个
3:0 ^ 3 = 3 - 遍历第二个
3:3 ^ 3 = 0 - 遍历
4:0 ^ 4 = 4 - 遍历第一个
5:4 ^ 5 = 1 - 遍历第二个
5:1 ^ 5 = 4
最终 uniqueNumber = 4,即只出现一次的数字。
代码详解
public class Main {
public static int solution(int[] inp) {
int uniqueNumber = 0; // 初始化一个变量来存储最终的结果
for (int num : inp) { // 遍历输入的数组
uniqueNumber ^= num; // 对每个数字进行异或操作
}
return uniqueNumber; // 返回最终找到的单独数字
}
public static void main(String[] args) {
// 测试样例
System.out.println(solution(new int[] { 1, 1, 2, 2, 3, 3, 4, 5, 5 }) == 4); // 输出: true
System.out.println(solution(new int[] { 0, 1, 0, 1, 2 }) == 2); // 输出: true
System.out.println(solution(new int[] { 7, 3, 3, 7, 10 }) == 10); // 输出: true
}
}
- 初始化:
uniqueNumber用于存储最终结果,初始为 0。 - 遍历数组: 使用增强型 for 循环遍历数组中的每个数字。
- 异或操作: 对每个数字进行异或操作,最终
uniqueNumber会保留只出现一次的数字。 - 返回结果: 返回
uniqueNumber。
知识点总结
-
位运算:
- 异或运算在处理成对出现的数据时非常有效。
- 其他位运算操作(如与、或、非)也有各自的用途。
-
时间复杂度:
- 本算法的时间复杂度为 O(n),因为它只需要遍历数组一次。
-
空间复杂度:
- 本算法使用了常数空间(仅一个变量),空间复杂度为 O(1)。
-
算法优化:
- 通过利用位运算的性质,可以在不使用额外空间的情况下解决问题。
学习建议
-
理解基础概念:
- 深入理解位运算的各种操作及其性质。
- 掌握常见的数据结构(如数组、链表、哈希表)及其应用场景。
-
动手实践:
- 多做练习题,尝试不同的算法和数据结构。
- 尝试自己实现算法,并进行调试和优化。
-
分析问题:
- 学会分析问题的本质,找到问题的关键点。
- 尝试将复杂问题分解成更小的子问题。
-
学习资源:
- 阅读经典算法书籍,如《算法导论》。
- 在线学习平台,如 LeetCode、牛客网等,提供丰富的练习题和讨论。
-
高效学习方法:
- 主动学习: 主动思考问题,而不是被动接受知识。
- 总结归纳: 定期总结所学知识,形成自己的知识体系。
- 实践应用: 将所学知识应用到实际问题中,加深理解。
- 持续学习: 保持学习的热情,不断学习新的知识和技能。
总结
通过这道题,我们学习了如何使用位运算中的异或操作在 O(n) 的时间复杂度和常数空间复杂度内找到只出现一次的数字。这种方法不仅高效,而且简洁,是解决类似问题的有效手段。希望通过这篇解析,能够帮助大家更好地理解和掌握位运算的应用。