1.找单独的数 | 豆包MarsCode AI 刷题

6 阅读3分钟

题目解析

在班级中的 "独特卡片数字" 问题要求找到唯一只出现一次的数字,其余所有数字都出现了两次。这是一个典型的 "异或" 问题,利用了异或运算的性质,结合时间复杂度 O(n) 和空间复杂度 O(1) 的要求,设计算法。

题目分析

  1. 输入输出:

    • 输入是一维整数数组 cards,长度为奇数,满足 ( 1 \leq \text{cards.length} \leq 1001 )。
    • 输出为唯一只出现一次的数字。
  2. 约束条件:

    • 数组中除了一个数字,其余数字均恰好出现两次。
    • 每个数字的取值范围为 ( 0 \leq \text{cards[i]} \leq 1000 )。
  3. 数学背景:

    • 异或运算 ^ 具有以下重要性质:
      • ( a \oplus a = 0 ) (任意数异或自身结果为 0)。
      • ( a \oplus 0 = a ) (任意数异或 0 结果为自身)。
      • 异或满足交换律与结合律:( a \oplus b \oplus c = (a \oplus c) \oplus b )。
    • 如果将数组中所有数字异或,成对出现的数字会相互抵消,最终结果即为只出现一次的数字。

解题思路

本题的关键在于利用异或的特性简化问题:

  1. 初始化结果变量 result = 0
  2. 遍历数组,将每个数字依次与 result 异或。
  3. 遍历结束时,result 中存储的即为唯一出现一次的数字。

算法复杂度

  1. 时间复杂度:
    • 遍历数组一次,时间复杂度为 O(n)。
  2. 空间复杂度:
    • 只使用一个额外变量 result,空间复杂度为 O(1)。

图解

假设输入数组为 [1, 1, 2, 2, 3, 3, 4, 5, 5],我们通过异或操作依次更新 result 的值:

步骤当前数字当前 result计算过程
初始-0-
111( 0 \oplus 1 )
210( 1 \oplus 1 )
322( 0 \oplus 2 )
420( 2 \oplus 2 )
533( 0 \oplus 3 )
630( 3 \oplus 3 )
744( 0 \oplus 4 )
851( 4 \oplus 5 )
954( 1 \oplus 5 )

最终结果为 4。

代码实现

# 方法:异或法

def solution(cards):
    result = 0

    for num in cards:
        result ^= num  # 将当前数字与结果异或

    return result

# 测试用例
if __name__ == "__main__":
    # 示例1
    assert solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4
    # 示例2
    assert solution([0, 1, 0, 1, 2]) == 2
    # 示例3
    assert solution([7, 3, 3, 7, 10]) == 10

    print("所有测试用例通过!")

拓展思考

异或法的优雅之处在于它不仅解决了本题,还可以用于其他类似问题,比如:

  1. 找数组中两个不重复的数字:
    • 如果数组中有两个不重复的数字,则需要分组异或以区分它们。
  2. 判断数组是否存在重复数字:
    • 将所有数字和从 ( 1 ) 到 ( n ) 的自然数异或,若结果不为 0,说明存在重复。

思考

  1. 使用位运算的优越性:

    • 相比哈希表法,位运算省去了额外的空间。
    • 在硬件级别,异或操作非常高效,适合处理大规模数据。
  2. 可扩展性:

    • 如果输入数据中数字范围增大(如 ( 10^9 ) 级别),异或法仍然适用,而哈希表可能因内存消耗过高而失效。
  3. 注意边界条件:

    • 输入数组长度为 1 的情况,直接返回第一个数字。
    • 数据为空时(理论上不会出现),需添加额外的错误处理逻辑。

本题通过数学特性优化代码的重要性,将问题从一般的时间复杂度 O(n^2) 简化到 O(n),并通过精简操作达成最优解。