青训营X豆包MarsCode 找单独的数 题解 | 豆包MarsCode AI刷题

57 阅读5分钟

找单独的数

问题描述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。

要求:

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力。

测试样例

样例1:

输入:cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
解释:拿到数字 4 的同学是唯一一个没有配对的。

样例2:

输入:cards = [0, 1, 0, 1, 2]
输出:2
解释:数字 2 只出现一次,是独特的卡片。

样例3:

输入:cards = [7, 3, 3, 7, 10]
输出:10
解释:10 是班级中唯一一个不重复的数字卡片。

约束条件

  • 1 ≤ cards.length ≤ 1001
  • 0 ≤ cards[i] ≤ 1000
  • 班级人数为奇数
  • 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次

Code:

    // 核心逻辑:通过异或运算找出数组中只出现一次的数字
    public static int solution(int[] inp) {
        // 初始化结果变量为0,所有数字会与它逐步异或
        int result = 0;

        // 遍历数组,使用异或运算抵消成对出现的数字
        for (int p : inp) {
            result ^= p; // 异或当前数字
        }

        // 返回最终结果,即只出现一次的数字
        return result;
    }
 
    public static void main(String[] args) {
        // 测试用例,验证算法的正确性
        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4); // 数组中唯一的数字是4
        System.out.println(solution(new int[]{0, 1, 0, 1, 2}) == 2); // 数组中唯一的数字是2
    }
}

示例流程

假设输入数组为 {1, 1, 2, 2, 3, 3, 4}

  • 初始状态:result = 0

  • 遍历过程:

    • 第1步:result = 0 ^ 1 = 1
    • 第2步:result = 1 ^ 1 = 0(第一个 1 与第二个 1 被抵消)
    • 第3步:result = 0 ^ 2 = 2
    • 第4步:result = 2 ^ 2 = 0(两个 2 被抵消)
    • 第5步:result = 0 ^ 3 = 3
    • 第6步:result = 3 ^ 3 = 0(两个 3 被抵消)
    • 第7步:result = 0 ^ 4 = 4(最终保留 4
  • 输出:4

一.思路介绍

  1. 代码功能说明

这段代码的功能是:

  • 找出一个整数数组中只出现一次的元素,假设数组中只有一个这样的元素,其它元素均出现两次。
  • 代码通过异或运算实现这一目标,使用的是位操作的特性。

2. 核心算法思路

异或运算 ^ 是位运算中的一种。其特点如下:

  1. 交换律a ^ b == b ^ a
  2. 结合律(a ^ b) ^ c == a ^ (b ^ c)
  3. 自身异或为零a ^ a == 0
  4. 零与任意数异或为数本身a ^ 0 == a

基于以上特性:

  • 如果数组中的数字按位异或,成对出现的数字会抵消为 0,因为 a ^ a = 0
  • 最终结果只剩下那个只出现一次的数字。

实现流程

  1. 初始化 result = 0
  2. 遍历数组,将每个数字与 result 进行异或。
  3. 遍历完成后,result 即为数组中只出现一次的数字。

二. 总结

  • 效率
    这段代码的时间复杂度为 O(n) ,因为只需要一次遍历数组。
    空间复杂度为 O(1) ,无需额外的数据结构存储中间结果。相较于哈希表或集合记录频次的方法,这种方案非常高效。

  • 简洁性
    利用异或运算的数学特性,代码非常简洁。避免了复杂的条件判断或额外的数据结构操作。

  • 边界情况的处理
    代码对边界情况天然适应,例如:

    • 数组中只有一个数字(直接返回该数字)。
    • 所有数字均成对出现(结果为 0)。

三.个人理解

  1. 选择异或运算的优越性

    • 异或运算的特性非常适合这种“一对一抵消”的场景。
    • 时间复杂度是 O(n) ,只需要遍历一次数组,非常高效。
    • 空间复杂度是 O(1) ,不需要额外的数据结构,比用哈希表记录数字频次更节约资源。
  2. 与其他方法的对比

    • 如果不用异或运算,也可以用其他方法,如:

      • 哈希表:记录每个数字的出现次数,最后找到频次为 1 的数字。但哈希表需要额外的存储空间,空间复杂度为 O(n)
      • 排序:对数组排序,然后找出唯一未成对的数字。排序的时间复杂度为 O(n log n)
    • 异或运算是最优解之一,尤其适合大规模数据或嵌入式环境。

  3. 适用范围

    • 当前方法假设数组中只有一个数字是单独出现,其余数字成对出现。如果有多个独立数字或者出现三次以上的数字,这种方法就无法解决问题,需要改用其他算法(如位运算的分组法)。
  4. 通用性与扩展性

    • 这种算法思路不仅限于查找单个唯一数字,还可以扩展到其他问题。例如:

      • 找出两个唯一数字:通过分组异或来解决。
      • 检查数据完整性:异或操作常被用作校验码的一部分。