青训营X豆包MarsCode 技术训练营:找单独的数Python题解 | 豆包MarsCode AI 刷题

120 阅读4分钟

找单独的数

问题描述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小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
  • 班级人数为奇数
  • 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次

解题思路

题目要求找出在数组中只出现一次的数字,其他数字都恰好出现两次。利用位运算的 异或操作 可以在 O(n) 时间复杂度内解决这个问题。异或操作的特点是:a ^ a = 0a ^ 0 = a。在数组中,所有成对出现的数字异或后会抵消为 0,剩下的就是只出现一次的数字。

代码

def solution(inp):
    # Edit your code here
    n = len(inp)  # 获得全部数字的个数
    ans = inp[0]  # 先将答案定为数组第一个数字
    for i in range(1, n):  # 从第二个数字开始遍历整个数组
        ans = ans ^ inp[i]  # 使用异或操作,两个一样的数字异或后为0,最后会只剩下只有一个的数字
    return ans


if __name__ == "__main__":
    # Add your test cases here

    print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4)
    print(solution([0, 1, 0, 1, 2]) == 2)

关键代码解释

  • ans = inp[0]: 初始化 ans 为数组的第一个元素。之后会用 ans 来存储最后的结果。

  • ans = ans ^ inp[i]: 遍历数组的每个数字,使用 异或运算 来抵消所有成对出现的数字。由于成对的数字异或后会变成 0,最后 ans 只会保留那个没有配对的数字。

时间和空间复杂度

  • 时间复杂度:O(n),因为只遍历了一次数组。

  • 空间复杂度:O(1),只用了常数空间来存储结果。

扩展:如何找到数组中只出现一次的数字,当其他数字都出现了三次

这是在其他数字出现两次的情况下的一个扩展,这里其他数字出现了三次,这样的话就不能用异或操作来做了,因为这样无论如何都不能抵消掉三个相同的数字。 可以使用位运算来解答此题。可以通过统计所有数字每一位的1的出现次数,若某一位1的总数不是3的倍数,那么独特数字在该位上是1。 扩展代码如下:

def solution(inp):
    result = 0
    for i in range(32):  # 对每一位(从第 0 位到第 31 位)进行计算
        bit_sum = 0
        for num in inp:
            bit_sum += (num >> i) & 1  # 统计第 i 位的1的数量
        if bit_sum % 3 != 0:  # 如果第 i 位的1的总数不是 3 的倍数
            result |= (1 << i)  # 说明独特数字在该位上有 1,更新到结果中
    # 考虑 Python 整数的有符号处理
    if result >= 2**31:
        result -= 2**32
    return result
  • for i in range(32): 遍历每一个二进制位(32 位整数)。

  • bit_sum += (num >> i) & 1: 计算所有数字在第 i 位上的 1 的数量。

  • if bit_sum % 3 != 0: 如果 bit_sum 不是 3 的倍数,说明该位上的 1 是由只出现一次的数字贡献的。

  • result |= (1 << i): 将第 i 位上的 1 更新到 result 中。

  • 最后一步处理 Python 整数的有符号情况。