刷题笔记-找单独的数 | 豆包MarsCodeAI刷题

118 阅读7分钟

找单独的数

题目描述

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

image.png

要求:

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

思考过程

异或运算(XOR)的特性

  • 相同为0,不同为1:对于任何整数 x,x ^ x = 0x ^ 0 = x
  • 交换律和结合律a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b

利用这些特性,我们可以设计一个算法来找到数组中唯一出现一次的数字。具体步骤如下:

解题思路

  1. 初始化一个变量 result 为 0:这个变量将用于存储最终的异或结果。
  2. 遍历数组中的每一个元素:将 result 与当前元素进行异或操作。
  3. 遍历结束后,result 就是只出现一次的数字:因为所有出现两次的数字会相互抵消,最终剩下的就是只出现一次的数字。

代码实现

def solution(cards):
    result = 0  # 初始化结果为0
    for card in cards:
        result ^= card  # 对每个元素进行异或操作
    return result  # 返回最终结果

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

模拟过程

  • 初始化 result 为 0:任何数与 0 异或结果还是它本身。
  • 遍历数组:对数组中的每个元素进行异或操作。
  • 例如,对于数组 [1, 1, 2, 2, 3, 3, 4, 5, 5]
    • result = 0 ^ 1 = 1
    • result = 1 ^ 1 = 0
    • result = 0 ^ 2 = 2
    • result = 2 ^ 2 = 0
    • result = 0 ^ 3 = 3
    • result = 3 ^ 3 = 0
    • result = 0 ^ 4 = 4
    • result = 4 ^ 5 = 1
    • result = 1 ^ 5 = 4
最终 result 为 4,即唯一出现一次的数字。

总结

通过异或巧妙实现可以在 O(n) 的时间复杂度和 O(1) 的空间复杂度内解决这个问题。

关键点:

  1. 时间复杂度 O(n)

    • 由于我们只需要遍历数组一次,对于每个元素进行一次异或操作,因此算法的时间复杂度为 O(n)。这意味着即使在处理大规模数据时,算法也能够快速完成任务。
  2. 空间复杂度 O(1)

    • 该算法只使用了一个额外的变量来储存异或的结果,而不需要使用额外的数组或数据结构来存储中间结果。因此,空间复杂度为 O(1),使得这个算法在内存使用上非常高效。
  3. 异或运算特性

    • 异或运算具有以下重要性质:
      • 相同的数异或为 0:x ^ x = 0
      • 任意数与 0 异或为其本身:x ^ 0 = x
      • 交换律和结合律:这使得运算的顺序不影响结果。
    • 这些性质意味着我们无需跟踪每个数字出现的次数,而只需计算每个数字的异或结果,最终剩下的就是只出现一次的数字。
  4. 适用场景

    • 此方法不仅适用于找出唯一的元素,还能够扩展到其他相关问题,如找出单一重复元素、判断是否存在重复元素等。只需稍作修改,就可以满足不同问题的需求。
  5. 实际应用

    • 这种算法在实际编程中非常有效,特别是在需要优化时间和空间复杂性的场景下。它在数据处理、网络包分析等领域有广泛的应用。

举一反三

掌握一种解题方法不仅仅是解决单个问题的钥匙,更是打开一类问题的工具。通过理解异或运算的特性,我们不仅能够高效地解决特定的问题,还能够举一反三,应用于其他相关的问题。在编程和算法的学习过程中,这种能力极为重要,因为许多问题的解决思路都是相通的。

以下是一些常见的问题描述,这些问题表面上可能不涉及异或运算,但实际上可以通过异或运算来更高效地解决。这种思维方式不仅帮助我们在面对此类问题时迅速找到解决方案,还能加深对异或运算及其他位操作的理解和应用。掌握这些技巧将使我们在数据结构和算法的学习中更加游刃有余。

示例问题

  1. 找出数组中唯一的元素
    在一个整数数组中,除了一个数字外,其他数字都出现了两次。找出那个只出现一次的数字。

  2. 找到数组中的单一重复元素
    在一个整数数组中,除了一个元素外,其他元素都只出现一次。找出这个重复的元素。

  3. 判断数组中是否包含重复元素
    给定一个整数数组,判断其中是否包含重复元素。利用异或运算可以帮助简化这个过程。

示例解答

1. 找出数组中唯一的元素

问题描述

在一个整数数组中,除了一个数字外,其他数字都出现了两次。找出那个只出现一次的数字。

代码实现
def find_unique_number(nums):
    unique = 0
    for num in nums:
        unique ^= num  # 利用异或的性质:相同的数异或为0,0与任何数异或为其本身
    return unique

# 测试样例
nums = [4, 1, 2, 1, 2]
output = find_unique_number(nums)
print(output)  # 输出:4

2. 找到数组中的单一重复元素

问题描述

在一个整数数组中,除了一个元素外,其他元素都只出现一次。找出这个重复的元素。

解决思路

通过异或运算结合数组的元素计数帮助找到重复元素。

代码实现
def find_single_duplicate(nums):
    seen = 0
    for num in nums:
        seen ^= num  # 逐步计算异或,重复的元素最终结果会消失
    return seen

# 测试样例
nums = [1, 2, 3, 1]
output = find_single_duplicate(nums)
print(output)  # 输出:1

3. 找出数组中重复的元素

问题描述

给定一个整数数组,判断其中是否包含重复元素。

解决思路

可以使用异或运算来帮助确定是否有相同的元素。

代码实现
def contains_duplicate(nums):
    seen = 0  # 通过位操作记录已见元素
    for num in nums:
        mask = 1 << num  # 创建掩码
        if seen & mask:  # 如果掩码已存在,说明重复
            return True
        seen |= mask  # 将掩码添加到已见的集合中
    return False

# 测试样例
nums = [1, 2, 3, 1]
output = contains_duplicate(nums)
print(output)  # 输出:True

不同位操作总结

操作问题描述使用示例
异或找出只出现一次的数字在数组中找出唯一元素
找到数组中的单一重复元素在数组中找出一个重复的元素
判断数组中是否存在重复元素判断是否有元素出现超过一次
交集找出两个数组的共同元素找出两个学生班级的共同同学
两个集合的交集获取两组数据中都存在的项
并集合并两个数组将两组学生的名单合并成一个没有重复的名单
获取所有元素获取两组数据的所有不同项
差集找出在A中但不在B中的元素找出某班级中不在另一个班级中的学生
获取不重复元素从两个数据集中找出一个数据集存在的而另一个不存在的项

通过这些示例和总结,可以看到不同位操作在解决问题时的灵活性和高效性。