青训营X豆包MarsCode 技术训练营第一课 | 豆包MarsCode AI 刷题

74 阅读4分钟

解析与图解

问题描述

在一个班级中,每个学生都拿到了一张卡片,上面有一个整数。已知除了一个数字外,其他所有数字都恰好出现了两次。目标是设计一个算法,在时间复杂度为 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)**操作。

异或运算的性质

  1. 自反性: 任何数与自身异或结果为 0,即 a ^ a = 0
  2. 交换律: a ^ b = b ^ a
  3. 结合律: (a ^ b) ^ c = a ^ (b ^ c)
  4. 恒等元: 任何数与 0 异或结果为其自身,即 a ^ 0 = a

基于这些性质,我们可以得出以下结论:

  • 如果一个数组中所有数字都成对出现,那么所有数字异或的结果为 0。
  • 如果有一个数字只出现一次,而其他数字都成对出现,那么所有数字异或的结果就是那个只出现一次的数字。

算法步骤

  1. 初始化一个变量 uniqueNumber 为 0。
  2. 遍历数组中的每个数字 num
    • uniqueNumbernum 进行异或操作:uniqueNumber ^= num
  3. 遍历结束后,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

知识点总结

  1. 位运算:

    • 异或运算在处理成对出现的数据时非常有效。
    • 其他位运算操作(如与、或、非)也有各自的用途。
  2. 时间复杂度:

    • 本算法的时间复杂度为 O(n),因为它只需要遍历数组一次。
  3. 空间复杂度:

    • 本算法使用了常数空间(仅一个变量),空间复杂度为 O(1)。
  4. 算法优化:

    • 通过利用位运算的性质,可以在不使用额外空间的情况下解决问题。

学习建议

  1. 理解基础概念:

    • 深入理解位运算的各种操作及其性质。
    • 掌握常见的数据结构(如数组、链表、哈希表)及其应用场景。
  2. 动手实践:

    • 多做练习题,尝试不同的算法和数据结构。
    • 尝试自己实现算法,并进行调试和优化。
  3. 分析问题:

    • 学会分析问题的本质,找到问题的关键点。
    • 尝试将复杂问题分解成更小的子问题。
  4. 学习资源:

    • 阅读经典算法书籍,如《算法导论》。
    • 在线学习平台,如 LeetCode、牛客网等,提供丰富的练习题和讨论。
  5. 高效学习方法:

    • 主动学习: 主动思考问题,而不是被动接受知识。
    • 总结归纳: 定期总结所学知识,形成自己的知识体系。
    • 实践应用: 将所学知识应用到实际问题中,加深理解。
    • 持续学习: 保持学习的热情,不断学习新的知识和技能。

总结

通过这道题,我们学习了如何使用位运算中的异或操作在 O(n) 的时间复杂度和常数空间复杂度内找到只出现一次的数字。这种方法不仅高效,而且简洁,是解决类似问题的有效手段。希望通过这篇解析,能够帮助大家更好地理解和掌握位运算的应用。